306 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			306 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GTK - The GIMP Toolkit
 | |
|  * Copyright (C) 2012, One Laptop Per Child.
 | |
|  * Copyright (C) 2014, Red Hat, Inc.
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * Author(s): Carlos Garnacho <carlosg@gnome.org>
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * SECTION:gtkgestureswipe
 | |
|  * @Short_description: Swipe gesture
 | |
|  * @Title: GtkGestureSwipe
 | |
|  *
 | |
|  * #GtkGestureSwipe is a #GtkGesture implementation able to recognize
 | |
|  * swipes, after a press/move/.../move/release sequence happens, the
 | |
|  * #GtkGestureSwipe::swipe signal will be emitted, providing the velocity
 | |
|  * and directionality of the sequence at the time it was lifted.
 | |
|  *
 | |
|  * If the velocity is desired in intermediate points,
 | |
|  * gtk_gesture_swipe_get_velocity() can be called on eg. a
 | |
|  * #GtkGesture::update handler.
 | |
|  *
 | |
|  * All velocities are reported in pixels/sec units.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include "gtkgestureswipe.h"
 | |
| #include "gtkgestureswipeprivate.h"
 | |
| #include "gtkgestureprivate.h"
 | |
| #include "gtkmarshalers.h"
 | |
| #include "gtkintl.h"
 | |
| 
 | |
| #define CAPTURE_THRESHOLD_MS 150
 | |
| 
 | |
| typedef struct _GtkGestureSwipePrivate GtkGestureSwipePrivate;
 | |
| typedef struct _EventData EventData;
 | |
| 
 | |
| struct _EventData
 | |
| {
 | |
|   guint32 evtime;
 | |
|   GdkPoint point;
 | |
| };
 | |
| 
 | |
| struct _GtkGestureSwipePrivate
 | |
| {
 | |
|   GArray *events;
 | |
| };
 | |
| 
 | |
| enum {
 | |
|   SWIPE,
 | |
|   N_SIGNALS
 | |
| };
 | |
| 
 | |
| static guint signals[N_SIGNALS] = { 0 };
 | |
| 
 | |
| G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureSwipe, gtk_gesture_swipe, GTK_TYPE_GESTURE_SINGLE)
 | |
| 
 | |
| static void
 | |
| gtk_gesture_swipe_finalize (GObject *object)
 | |
| {
 | |
|   GtkGestureSwipePrivate *priv;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (GTK_GESTURE_SWIPE (object));
 | |
|   g_array_free (priv->events, TRUE);
 | |
| 
 | |
|   G_OBJECT_CLASS (gtk_gesture_swipe_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gtk_gesture_swipe_filter_event (GtkEventController *controller,
 | |
|                                 const GdkEvent     *event)
 | |
| {
 | |
|   /* Let touchpad swipe events go through, only if they match n-points  */
 | |
|   if (event->type == GDK_TOUCHPAD_SWIPE)
 | |
|     {
 | |
|       guint n_points;
 | |
| 
 | |
|       g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
 | |
| 
 | |
|       if (event->touchpad_swipe.n_fingers == n_points)
 | |
|         return FALSE;
 | |
|       else
 | |
|         return TRUE;
 | |
|     }
 | |
| 
 | |
|   return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_swipe_parent_class)->filter_event (controller, event);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_gesture_swipe_clear_backlog (GtkGestureSwipe *gesture,
 | |
|                                   guint32          evtime)
 | |
| {
 | |
|   GtkGestureSwipePrivate *priv;
 | |
|   gint i, length = 0;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (gesture);
 | |
| 
 | |
|   for (i = 0; i < (gint) priv->events->len; i++)
 | |
|     {
 | |
|       EventData *data;
 | |
| 
 | |
|       data = &g_array_index (priv->events, EventData, i);
 | |
| 
 | |
|       if (data->evtime >= evtime - CAPTURE_THRESHOLD_MS)
 | |
|         {
 | |
|           length = i - 1;
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (length > 0)
 | |
|     g_array_remove_range (priv->events, 0, length);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_gesture_swipe_update (GtkGesture       *gesture,
 | |
|                           GdkEventSequence *sequence)
 | |
| {
 | |
|   GtkGestureSwipe *swipe = GTK_GESTURE_SWIPE (gesture);
 | |
|   GtkGestureSwipePrivate *priv;
 | |
|   EventData new;
 | |
|   gdouble x, y;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (swipe);
 | |
|   _gtk_gesture_get_last_update_time (gesture, sequence, &new.evtime);
 | |
|   gtk_gesture_get_point (gesture, sequence, &x, &y);
 | |
| 
 | |
|   new.point.x = x;
 | |
|   new.point.y = y;
 | |
| 
 | |
|   _gtk_gesture_swipe_clear_backlog (swipe, new.evtime);
 | |
|   g_array_append_val (priv->events, new);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_gesture_swipe_calculate_velocity (GtkGestureSwipe *gesture,
 | |
|                                        gdouble         *velocity_x,
 | |
|                                        gdouble         *velocity_y)
 | |
| {
 | |
|   GtkGestureSwipePrivate *priv;
 | |
|   GdkEventSequence *sequence;
 | |
|   guint32 evtime, diff_time;
 | |
|   EventData *start, *end;
 | |
|   gdouble diff_x, diff_y;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (gesture);
 | |
|   *velocity_x = *velocity_y = 0;
 | |
| 
 | |
|   sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
 | |
|   _gtk_gesture_get_last_update_time (GTK_GESTURE (gesture), sequence, &evtime);
 | |
|   _gtk_gesture_swipe_clear_backlog (gesture, evtime);
 | |
| 
 | |
|   if (priv->events->len == 0)
 | |
|     return;
 | |
| 
 | |
|   start = &g_array_index (priv->events, EventData, 0);
 | |
|   end = &g_array_index (priv->events, EventData, priv->events->len - 1);
 | |
| 
 | |
|   diff_time = end->evtime - start->evtime;
 | |
|   diff_x = end->point.x - start->point.x;
 | |
|   diff_y = end->point.y - start->point.y;
 | |
| 
 | |
|   if (diff_time == 0)
 | |
|     return;
 | |
| 
 | |
|   /* Velocity in pixels/sec */
 | |
|   *velocity_x = diff_x * 1000 / diff_time;
 | |
|   *velocity_y = diff_y * 1000 / diff_time;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_gesture_swipe_end (GtkGesture       *gesture,
 | |
|                        GdkEventSequence *sequence)
 | |
| {
 | |
|   GtkGestureSwipe *swipe = GTK_GESTURE_SWIPE (gesture);
 | |
|   GtkGestureSwipePrivate *priv;
 | |
|   gdouble velocity_x, velocity_y;
 | |
|   GdkEventSequence *seq;
 | |
| 
 | |
|   seq = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
 | |
| 
 | |
|   if (gtk_gesture_get_sequence_state (gesture, seq) == GTK_EVENT_SEQUENCE_DENIED)
 | |
|     return;
 | |
| 
 | |
|   if (gtk_gesture_is_active (gesture))
 | |
|     return;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (swipe);
 | |
|   _gtk_gesture_swipe_calculate_velocity (swipe, &velocity_x, &velocity_y);
 | |
|   g_signal_emit (gesture, signals[SWIPE], 0, velocity_x, velocity_y);
 | |
| 
 | |
|   if (priv->events->len > 0)
 | |
|     g_array_remove_range (priv->events, 0, priv->events->len);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_gesture_swipe_class_init (GtkGestureSwipeClass *klass)
 | |
| {
 | |
|   GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);
 | |
|   GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 | |
| 
 | |
|   object_class->finalize = gtk_gesture_swipe_finalize;
 | |
| 
 | |
|   event_controller_class->filter_event = gtk_gesture_swipe_filter_event;
 | |
| 
 | |
|   gesture_class->update = gtk_gesture_swipe_update;
 | |
|   gesture_class->end = gtk_gesture_swipe_end;
 | |
| 
 | |
|   /**
 | |
|    * GtkGestureSwipe::swipe:
 | |
|    * @gesture: object which received the signal
 | |
|    * @velocity_x: velocity in the X axis, in pixels/sec
 | |
|    * @velocity_y: velocity in the Y axis, in pixels/sec
 | |
|    *
 | |
|    * This signal is emitted when the recognized gesture is finished, velocity
 | |
|    * and direction are a product of previously recorded events.
 | |
|    *
 | |
|    * Since: 3.14
 | |
|    */
 | |
|   signals[SWIPE] =
 | |
|     g_signal_new (I_("swipe"),
 | |
|                   G_TYPE_FROM_CLASS (klass),
 | |
|                   G_SIGNAL_RUN_LAST,
 | |
|                   G_STRUCT_OFFSET (GtkGestureSwipeClass, swipe),
 | |
|                   NULL, NULL, NULL,
 | |
|                   G_TYPE_NONE, 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_gesture_swipe_init (GtkGestureSwipe *gesture)
 | |
| {
 | |
|   GtkGestureSwipePrivate *priv;
 | |
| 
 | |
|   priv = gtk_gesture_swipe_get_instance_private (gesture);
 | |
|   priv->events = g_array_new (FALSE, FALSE, sizeof (EventData));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gtk_gesture_swipe_new:
 | |
|  * @widget: a #GtkWidget
 | |
|  *
 | |
|  * Returns a newly created #GtkGesture that recognizes swipes.
 | |
|  *
 | |
|  * Returns: a newly created #GtkGestureSwipe
 | |
|  *
 | |
|  * Since: 3.14
 | |
|  **/
 | |
| GtkGesture *
 | |
| gtk_gesture_swipe_new (GtkWidget *widget)
 | |
| {
 | |
|   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
 | |
| 
 | |
|   return g_object_new (GTK_TYPE_GESTURE_SWIPE,
 | |
|                        "widget", widget,
 | |
|                        NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gtk_gesture_swipe_get_velocity:
 | |
|  * @gesture: a #GtkGestureSwipe
 | |
|  * @velocity_x: (out): return value for the velocity in the X axis, in pixels/sec
 | |
|  * @velocity_y: (out): return value for the velocity in the Y axis, in pixels/sec
 | |
|  *
 | |
|  * If the gesture is recognized, this function returns %TRUE and fill in
 | |
|  * @velocity_x and @velocity_y with the recorded velocity, as per the
 | |
|  * last event(s) processed.
 | |
|  *
 | |
|  * Returns: whether velocity could be calculated
 | |
|  *
 | |
|  * Since: 3.14
 | |
|  **/
 | |
| gboolean
 | |
| gtk_gesture_swipe_get_velocity (GtkGestureSwipe *gesture,
 | |
|                                 gdouble         *velocity_x,
 | |
|                                 gdouble         *velocity_y)
 | |
| {
 | |
|   gdouble vel_x, vel_y;
 | |
| 
 | |
|   g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
 | |
| 
 | |
|   if (!gtk_gesture_is_recognized (GTK_GESTURE (gesture)))
 | |
|     return FALSE;
 | |
| 
 | |
|   _gtk_gesture_swipe_calculate_velocity (gesture, &vel_x, &vel_y);
 | |
| 
 | |
|   if (velocity_x)
 | |
|     *velocity_x = vel_x;
 | |
|   if (velocity_y)
 | |
|     *velocity_y = vel_y;
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | 
