[gimp] app: Implement canvas support for touchpad gesture rotation by pinch



commit e8cf57f1579305cc77226e6c3dee6b5594a635bf
Author: Povilas Kanapickas <povilas radix lt>
Date:   Wed Mar 16 23:59:20 2022 +0200

    app: Implement canvas support for touchpad gesture rotation by pinch

 app/display/gimpdisplayshell-tool-events.c | 64 ++++++++++++++++++++++++++++++
 app/display/gimpdisplayshell-tool-events.h |  7 ++++
 app/display/gimpdisplayshell.c             | 12 ++++++
 app/display/gimpdisplayshell.h             |  5 +++
 4 files changed, 88 insertions(+)
---
diff --git a/app/display/gimpdisplayshell-tool-events.c b/app/display/gimpdisplayshell-tool-events.c
index e28e7199f7..16df65ff58 100644
--- a/app/display/gimpdisplayshell-tool-events.c
+++ b/app/display/gimpdisplayshell-tool-events.c
@@ -21,6 +21,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
+#include "libgimpmath/gimpmath.h"
 #include "libgimpwidgets/gimpwidgets.h"
 
 #include "display-types.h"
@@ -100,6 +101,10 @@ static void       gimp_display_shell_handle_scrolling         (GimpDisplayShell
                                                                gint               x,
                                                                gint               y);
 
+static void       gimp_display_shell_rotate_gesture_maybe_get_state (GtkGestureRotate *gesture,
+                                                                     GdkEventSequence *sequence,
+                                                                     guint            *maybe_out_state);
+
 static void       gimp_display_shell_space_pressed            (GimpDisplayShell  *shell,
                                                                const GdkEvent    *event);
 static void       gimp_display_shell_released                 (GimpDisplayShell  *shell,
@@ -1273,6 +1278,38 @@ gimp_display_shell_zoom_gesture_update (GtkGestureZoom   *gesture,
                             GIMP_ZOOM_FOCUS_POINTER);
 }
 
+void
+gimp_display_shell_rotate_gesture_begin (GtkGestureRotate *gesture,
+                                         GdkEventSequence *sequence,
+                                         GimpDisplayShell *shell)
+{
+
+  shell->initial_gesture_rotate_angle = shell->rotate_angle;
+  shell->last_gesture_rotate_state    = 0;
+  gimp_display_shell_rotate_gesture_maybe_get_state (gesture, sequence,
+                                                     &shell->last_gesture_rotate_state);
+}
+
+void
+gimp_display_shell_rotate_gesture_update (GtkGestureRotate *gesture,
+                                          GdkEventSequence *sequence,
+                                          GimpDisplayShell *shell)
+{
+  gdouble         angle;
+  gboolean        constrain;
+
+  gimp_display_shell_rotate_gesture_maybe_get_state (gesture, sequence,
+                                                     &shell->last_gesture_rotate_state);
+
+  angle = shell->initial_gesture_rotate_angle +
+          180.0 * gtk_gesture_rotate_get_angle_delta (gesture) / G_PI;
+
+  constrain = (shell->last_gesture_rotate_state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+
+  gimp_display_shell_rotate_to (shell,
+                                constrain ? RINT (angle / 15.0) * 15.0 : angle);
+}
+
 void
 gimp_display_shell_buffer_stroke (GimpMotionBuffer *buffer,
                                   const GimpCoords *coords,
@@ -1671,6 +1708,33 @@ gimp_display_shell_handle_scrolling (GimpDisplayShell *shell,
   shell->scroll_last_y = y;
 }
 
+static void
+gimp_display_shell_rotate_gesture_maybe_get_state (GtkGestureRotate *gesture,
+                                                   GdkEventSequence *sequence,
+                                                   guint            *maybe_out_state)
+{
+  /* The only way to get any access to any data about events handled by the
+   * GtkGestureRotate is through its last_event. The set of events handled by
+   * GtkGestureRotate is not fully defined so we can't guarantee that last_event
+   * will be of event type that has a state field (though touch and gesture
+   * events do have that).
+   *
+   * Usually this would not be a problem, but when handling a gesture we don't
+   * want to repeatedly switch between a valid state and its default value if
+   * last_event happens to not have it. Thus we store the last valid state
+   * and only update it if we get a valid state from last_event.
+   */
+  guint           state = 0;
+  const GdkEvent *last_event;
+
+  last_event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
+  if (last_event == NULL)
+    return;
+
+  if (gdk_event_get_state (last_event, &state))
+    *maybe_out_state = state;
+}
+
 static void
 gimp_display_shell_space_pressed (GimpDisplayShell *shell,
                                   const GdkEvent   *event)
diff --git a/app/display/gimpdisplayshell-tool-events.h b/app/display/gimpdisplayshell-tool-events.h
index 6539640c11..a491999d04 100644
--- a/app/display/gimpdisplayshell-tool-events.h
+++ b/app/display/gimpdisplayshell-tool-events.h
@@ -37,6 +37,13 @@ void       gimp_display_shell_zoom_gesture_update     (GtkGestureZoom   *gesture
                                                        GdkEventSequence *sequence,
                                                        GimpDisplayShell *shell);
 
+void       gimp_display_shell_rotate_gesture_begin    (GtkGestureRotate *gesture,
+                                                       GdkEventSequence *sequence,
+                                                       GimpDisplayShell *shell);
+void       gimp_display_shell_rotate_gesture_update   (GtkGestureRotate *gesture,
+                                                       GdkEventSequence *sequence,
+                                                       GimpDisplayShell *shell);
+
 void       gimp_display_shell_buffer_stroke           (GimpMotionBuffer *buffer,
                                                        const GimpCoords *coords,
                                                        guint32           time,
diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c
index a87b940290..8ca3d1efa4 100644
--- a/app/display/gimpdisplayshell.c
+++ b/app/display/gimpdisplayshell.c
@@ -516,6 +516,10 @@ gimp_display_shell_constructed (GObject *object)
   gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (shell->zoom_gesture),
                                               GTK_PHASE_CAPTURE);
 
+  shell->rotate_gesture = gtk_gesture_rotate_new (GTK_WIDGET (shell->canvas));
+  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (shell->rotate_gesture),
+                                              GTK_PHASE_CAPTURE);
+
   /*  the horizontal ruler  */
   shell->hrule = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
   gtk_widget_set_events (GTK_WIDGET (shell->hrule),
@@ -608,6 +612,13 @@ gimp_display_shell_constructed (GObject *object)
   g_signal_connect (shell->zoom_gesture, "update",
                     G_CALLBACK (gimp_display_shell_zoom_gesture_update),
                     shell);
+  g_signal_connect (shell->rotate_gesture, "begin",
+                    G_CALLBACK (gimp_display_shell_rotate_gesture_begin),
+                    shell);
+  g_signal_connect (shell->rotate_gesture, "update",
+                    G_CALLBACK (gimp_display_shell_rotate_gesture_update),
+                    shell);
+
 
   /*  the zoom button  */
   shell->zoom_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
@@ -759,6 +770,7 @@ gimp_display_shell_dispose (GObject *object)
     }
 
   g_clear_object (&shell->zoom_gesture);
+  g_clear_object (&shell->rotate_gesture);
 
   g_clear_pointer (&shell->render_cache,       cairo_surface_destroy);
   g_clear_pointer (&shell->render_cache_valid, cairo_region_destroy);
diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h
index f9bf6360f0..7a4a0a41f3 100644
--- a/app/display/gimpdisplayshell.h
+++ b/app/display/gimpdisplayshell.h
@@ -101,6 +101,7 @@ struct _GimpDisplayShell
 
   GtkWidget         *canvas;           /*  GimpCanvas widget                  */
   GtkGesture        *zoom_gesture;     /*  Zoom gesture handler for the canvas*/
+  GtkGesture        *rotate_gesture;   /*  Rotation gesture handler           */
 
   GtkAdjustment     *hsbdata;          /*  adjustments                        */
   GtkAdjustment     *vsbdata;
@@ -203,6 +204,10 @@ struct _GimpDisplayShell
   /*  the state of gimp_display_shell_zoom_gesture_*() */
   gdouble            last_zoom_scale;
 
+  /*  the state of gimp_display_shell_rotate_gesture_*() */
+  guint              last_gesture_rotate_state;
+  gdouble            initial_gesture_rotate_angle;
+
   /* Two states are possible when the shell is grabbed: it can be
    * grabbed with space (or space+button1 which is the same state),
    * then if space is released but button1 was still pressed, we wait


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]