gtkgesturezoom.c 7.07 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* 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>
 */
20 21 22 23 24 25 26 27 28

/**
 * SECTION:gtkgesturezoom
 * @Short_description: Zoom gesture
 * @Title: GtkGestureZoom
 * @See_also: #GtkGestureRotate
 *
 * #GtkGestureZoom is a #GtkGesture implementation able to recognize
 * pinch/zoom gestures, whenever the distance between both tracked
Carlos Garnacho's avatar
Carlos Garnacho committed
29
 * sequences changes, the #GtkGestureZoom::scale-changed signal is
30 31 32
 * emitted to report the scale factor.
 */

Carlos Garnacho's avatar
Carlos Garnacho committed
33 34
#include "config.h"
#include <math.h>
35 36
#include "gtkgesturezoom.h"
#include "gtkgesturezoomprivate.h"
37
#include "gtkintl.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

typedef struct _GtkGestureZoomPrivate GtkGestureZoomPrivate;

enum {
  SCALE_CHANGED,
  LAST_SIGNAL
};

struct _GtkGestureZoomPrivate
{
  gdouble initial_distance;
};

static guint signals[LAST_SIGNAL] = { 0 };

G_DEFINE_TYPE_WITH_PRIVATE (GtkGestureZoom, gtk_gesture_zoom, GTK_TYPE_GESTURE)

static void
gtk_gesture_zoom_init (GtkGestureZoom *gesture)
{
}

static GObject *
gtk_gesture_zoom_constructor (GType                  type,
                              guint                  n_construct_properties,
                              GObjectConstructParam *construct_properties)
{
  GObject *object;

  object = G_OBJECT_CLASS (gtk_gesture_zoom_parent_class)->constructor (type,
                                                                        n_construct_properties,
                                                                        construct_properties);
  g_object_set (object, "n-points", 2, NULL);

  return object;
}

static gboolean
_gtk_gesture_zoom_get_distance (GtkGestureZoom *zoom,
                                gdouble        *distance)
{
79
  const GdkEvent *last_event;
Carlos Garnacho's avatar
Carlos Garnacho committed
80 81
  gdouble x1, y1, x2, y2;
  GtkGesture *gesture;
82
  GList *sequences = NULL;
Carlos Garnacho's avatar
Carlos Garnacho committed
83
  gdouble dx, dy;
84
  gboolean retval = FALSE;
Carlos Garnacho's avatar
Carlos Garnacho committed
85 86 87 88

  gesture = GTK_GESTURE (zoom);

  if (!gtk_gesture_is_recognized (gesture))
89
    goto out;
Carlos Garnacho's avatar
Carlos Garnacho committed
90 91

  sequences = gtk_gesture_get_sequences (gesture);
92
  if (!sequences)
93
    goto out;
Carlos Garnacho's avatar
Carlos Garnacho committed
94

95 96 97 98 99 100 101 102 103 104 105 106 107
  last_event = gtk_gesture_get_last_event (gesture, sequences->data);

  if (last_event->type == GDK_TOUCHPAD_PINCH &&
      (last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_BEGIN ||
       last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_UPDATE ||
       last_event->touchpad_pinch.phase == GDK_TOUCHPAD_GESTURE_PHASE_END))
    {
      /* Touchpad pinch */
      *distance = last_event->touchpad_pinch.scale;
    }
  else
    {
      if (!sequences->next)
108
        goto out;
109 110 111 112 113 114 115 116

      gtk_gesture_get_point (gesture, sequences->data, &x1, &y1);
      gtk_gesture_get_point (gesture, sequences->next->data, &x2, &y2);

      dx = x1 - x2;
      dy = y1 - y2;;
      *distance = sqrt ((dx * dx) + (dy * dy));
    }
Carlos Garnacho's avatar
Carlos Garnacho committed
117

118 119 120 121 122
  retval = TRUE;

 out:
  g_list_free (sequences);
  return retval;
Carlos Garnacho's avatar
Carlos Garnacho committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
}

static gboolean
_gtk_gesture_zoom_check_emit (GtkGestureZoom *gesture)
{
  GtkGestureZoomPrivate *priv;
  gdouble distance, zoom;

  if (!_gtk_gesture_zoom_get_distance (gesture, &distance))
    return FALSE;

  priv = gtk_gesture_zoom_get_instance_private (gesture);

  if (distance == 0 || priv->initial_distance == 0)
    return FALSE;

  zoom = distance / priv->initial_distance;
  g_signal_emit (gesture, signals[SCALE_CHANGED], 0, zoom);

  return TRUE;
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
static gboolean
gtk_gesture_zoom_filter_event (GtkEventController *controller,
                               const GdkEvent     *event)
{
  /* Let 2-finger touchpad pinch events go through */
  if (event->type == GDK_TOUCHPAD_PINCH)
    {
      if (event->touchpad_pinch.n_fingers == 2)
        return FALSE;
      else
        return TRUE;
    }

  return GTK_EVENT_CONTROLLER_CLASS (gtk_gesture_zoom_parent_class)->filter_event (controller, event);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
static void
gtk_gesture_zoom_begin (GtkGesture       *gesture,
                        GdkEventSequence *sequence)
{
  GtkGestureZoom *zoom = GTK_GESTURE_ZOOM (gesture);
  GtkGestureZoomPrivate *priv;

  priv = gtk_gesture_zoom_get_instance_private (zoom);
  _gtk_gesture_zoom_get_distance (zoom, &priv->initial_distance);
}

static void
gtk_gesture_zoom_update (GtkGesture       *gesture,
                         GdkEventSequence *sequence)
{
  _gtk_gesture_zoom_check_emit (GTK_GESTURE_ZOOM (gesture));
}

static void
gtk_gesture_zoom_class_init (GtkGestureZoomClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
183
  GtkEventControllerClass *event_controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
Carlos Garnacho's avatar
Carlos Garnacho committed
184 185 186 187
  GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (klass);

  object_class->constructor = gtk_gesture_zoom_constructor;

188 189
  event_controller_class->filter_event = gtk_gesture_zoom_filter_event;

Carlos Garnacho's avatar
Carlos Garnacho committed
190 191 192 193 194 195 196
  gesture_class->begin = gtk_gesture_zoom_begin;
  gesture_class->update = gtk_gesture_zoom_update;

  /**
   * GtkGestureZoom::scale-changed:
   * @controller: the object on which the signal is emitted
   * @scale: Scale delta, taking the initial state as 1:1
197 198 199 200 201
   *
   * This signal is emitted whenever the distance between both tracked
   * sequences changes.
   *
   * Since: 3.14
Carlos Garnacho's avatar
Carlos Garnacho committed
202 203
   */
  signals[SCALE_CHANGED] =
204
    g_signal_new (I_("scale-changed"),
Carlos Garnacho's avatar
Carlos Garnacho committed
205 206 207 208 209 210 211 212 213 214 215 216
                  GTK_TYPE_GESTURE_ZOOM,
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkGestureZoomClass, scale_changed),
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 1, G_TYPE_DOUBLE);
}

/**
 * gtk_gesture_zoom_new:
 * @widget: a #GtkWidget
 *
 * Returns a newly created #GtkGesture that recognizes zoom
Matthias Clasen's avatar
Matthias Clasen committed
217
 * in/out gestures (usually known as pinch/zoom).
Carlos Garnacho's avatar
Carlos Garnacho committed
218 219 220 221 222 223 224 225
 *
 * Returns: a newly created #GtkGestureZoom
 *
 * Since: 3.14
 **/
GtkGesture *
gtk_gesture_zoom_new (GtkWidget *widget)
{
226 227
  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);

Carlos Garnacho's avatar
Carlos Garnacho committed
228 229 230 231 232 233 234 235 236
  return g_object_new (GTK_TYPE_GESTURE_ZOOM,
                       "widget", widget,
                       NULL);
}

/**
 * gtk_gesture_zoom_get_scale_delta:
 * @gesture: a #GtkGestureZoom
 *
237 238 239
 * If @gesture is active, this function returns the zooming difference
 * since the gesture was recognized (hence the starting point is
 * considered 1:1). If @gesture is not active, 1 is returned.
Carlos Garnacho's avatar
Carlos Garnacho committed
240
 *
241
 * Returns: the scale delta
Carlos Garnacho's avatar
Carlos Garnacho committed
242 243 244
 *
 * Since: 3.14
 **/
245 246
gdouble
gtk_gesture_zoom_get_scale_delta (GtkGestureZoom *gesture)
Carlos Garnacho's avatar
Carlos Garnacho committed
247 248 249 250
{
  GtkGestureZoomPrivate *priv;
  gdouble distance;

251
  g_return_val_if_fail (GTK_IS_GESTURE_ZOOM (gesture), 1.0);
Carlos Garnacho's avatar
Carlos Garnacho committed
252 253

  if (!_gtk_gesture_zoom_get_distance (gesture, &distance))
254
    return 1.0;
Carlos Garnacho's avatar
Carlos Garnacho committed
255 256 257

  priv = gtk_gesture_zoom_get_instance_private (gesture);

258
  return distance / priv->initial_distance;
Carlos Garnacho's avatar
Carlos Garnacho committed
259
}