gtkgesturepan.c 9.82 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
/* GTK - The GIMP Toolkit
 * 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>
 */
19
20

/**
Matthias Clasen's avatar
Matthias Clasen committed
21
 * GtkGesturePan:
22
 *
Matthias Clasen's avatar
Matthias Clasen committed
23
 * `GtkGesturePan` is a `GtkGesture` for pan gestures.
24
 *
Matthias Clasen's avatar
Matthias Clasen committed
25
26
27
28
29
 * These are drags that are locked to happen along one axis. The axis
 * that a `GtkGesturePan` handles is defined at construct time, and
 * can be changed through [method@Gtk.GesturePan.set_orientation].
 *
 * When the gesture starts to be recognized, `GtkGesturePan` will
30
31
32
33
34
 * attempt to determine as early as possible whether the sequence
 * is moving in the expected direction, and denying the sequence if
 * this does not happen.
 *
 * Once a panning gesture along the expected axis is recognized,
Matthias Clasen's avatar
Matthias Clasen committed
35
36
 * the [signal@Gtk.GesturePan::pan] signal will be emitted as input
 * events are received, containing the offset in the given axis.
37
38
 */

Carlos Garnacho's avatar
Carlos Garnacho committed
39
#include "config.h"
40
41
#include "gtkgesturepan.h"
#include "gtkgesturepanprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
42
43
44
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkintl.h"
45
#include "gtkmarshalers.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

typedef struct _GtkGesturePanPrivate GtkGesturePanPrivate;

struct _GtkGesturePanPrivate
{
  guint orientation : 2;
  guint panning     : 1;
};

enum {
  PROP_ORIENTATION = 1
};

enum {
  PAN,
  N_SIGNALS
};

static guint signals[N_SIGNALS] = { 0 };

66
G_DEFINE_TYPE_WITH_PRIVATE (GtkGesturePan, gtk_gesture_pan, GTK_TYPE_GESTURE_DRAG)
Carlos Garnacho's avatar
Carlos Garnacho committed
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

static void
gtk_gesture_pan_get_property (GObject    *object,
                              guint       prop_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  GtkGesturePanPrivate *priv;

  priv = gtk_gesture_pan_get_instance_private (GTK_GESTURE_PAN (object));

  switch (prop_id)
    {
    case PROP_ORIENTATION:
      g_value_set_enum (value, priv->orientation);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
gtk_gesture_pan_set_property (GObject      *object,
                              guint         prop_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  switch (prop_id)
    {
    case PROP_ORIENTATION:
      gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (object),
                                       g_value_get_enum (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
106
107
direction_from_offset (double           offset_x,
                       double           offset_y,
108
109
                       GtkOrientation   orientation,
                       GtkPanDirection *direction)
Carlos Garnacho's avatar
Carlos Garnacho committed
110
{
111
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
Carlos Garnacho's avatar
Carlos Garnacho committed
112
113
114
115
116
117
    {
      if (offset_x > 0)
        *direction = GTK_PAN_DIRECTION_RIGHT;
      else
        *direction = GTK_PAN_DIRECTION_LEFT;
    }
118
  else if (orientation == GTK_ORIENTATION_VERTICAL)
Carlos Garnacho's avatar
Carlos Garnacho committed
119
120
121
122
123
124
125
126
127
128
129
130
    {
      if (offset_y > 0)
        *direction = GTK_PAN_DIRECTION_DOWN;
      else
        *direction = GTK_PAN_DIRECTION_UP;
    }
  else
    g_assert_not_reached ();
}

static gboolean
guess_direction (GtkGesturePan   *gesture,
131
132
                 double           offset_x,
                 double           offset_y,
Carlos Garnacho's avatar
Carlos Garnacho committed
133
134
                 GtkPanDirection *direction)
{
135
  double abs_x, abs_y;
Carlos Garnacho's avatar
Carlos Garnacho committed
136
137
138
139

  abs_x = ABS (offset_x);
  abs_y = ABS (offset_y);

140
#define FACTOR 2
Carlos Garnacho's avatar
Carlos Garnacho committed
141
142
  if (abs_x > abs_y * FACTOR)
    direction_from_offset (offset_x, offset_y,
143
                           GTK_ORIENTATION_HORIZONTAL, direction);
Carlos Garnacho's avatar
Carlos Garnacho committed
144
145
  else if (abs_y > abs_x * FACTOR)
    direction_from_offset (offset_x, offset_y,
146
                           GTK_ORIENTATION_VERTICAL, direction);
Carlos Garnacho's avatar
Carlos Garnacho committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  else
    return FALSE;

  return TRUE;
#undef FACTOR
}

static gboolean
check_orientation_matches (GtkGesturePan   *gesture,
                           GtkPanDirection  direction)
{
  GtkGesturePanPrivate *priv = gtk_gesture_pan_get_instance_private (gesture);

  return (((direction == GTK_PAN_DIRECTION_LEFT ||
            direction == GTK_PAN_DIRECTION_RIGHT) &&
162
           priv->orientation == GTK_ORIENTATION_HORIZONTAL) ||
Carlos Garnacho's avatar
Carlos Garnacho committed
163
164
          ((direction == GTK_PAN_DIRECTION_UP ||
            direction == GTK_PAN_DIRECTION_DOWN) &&
165
           priv->orientation == GTK_ORIENTATION_VERTICAL));
Carlos Garnacho's avatar
Carlos Garnacho committed
166
167
168
169
}

static void
gtk_gesture_pan_drag_update (GtkGestureDrag *gesture,
170
171
                             double          offset_x,
                             double          offset_y)
Carlos Garnacho's avatar
Carlos Garnacho committed
172
173
174
175
{
  GtkGesturePanPrivate *priv;
  GtkPanDirection direction;
  GtkGesturePan *pan;
176
  double offset;
Carlos Garnacho's avatar
Carlos Garnacho committed
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

  pan = GTK_GESTURE_PAN (gesture);
  priv = gtk_gesture_pan_get_instance_private (pan);

  if (!priv->panning)
    {
      if (!guess_direction (pan, offset_x, offset_y, &direction))
        return;

      if (!check_orientation_matches (pan, direction))
        {
          gtk_gesture_set_state (GTK_GESTURE (gesture),
                                 GTK_EVENT_SEQUENCE_DENIED);
          return;
        }

      priv->panning = TRUE;
    }
  else
    direction_from_offset (offset_x, offset_y, priv->orientation, &direction);

198
  offset = (priv->orientation == GTK_ORIENTATION_VERTICAL) ?
Carlos Garnacho's avatar
Carlos Garnacho committed
199
200
201
202
203
204
    ABS (offset_y) : ABS (offset_x);
  g_signal_emit (gesture, signals[PAN], 0, direction, offset);
}

static void
gtk_gesture_pan_drag_end (GtkGestureDrag *gesture,
205
206
                          double          offset_x,
                          double          offset_y)
Carlos Garnacho's avatar
Carlos Garnacho committed
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
{
  GtkGesturePanPrivate *priv;

  priv = gtk_gesture_pan_get_instance_private (GTK_GESTURE_PAN (gesture));
  priv->panning = FALSE;
}

static void
gtk_gesture_pan_class_init (GtkGesturePanClass *klass)
{
  GtkGestureDragClass *drag_gesture_class = GTK_GESTURE_DRAG_CLASS (klass);
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->get_property = gtk_gesture_pan_get_property;
  object_class->set_property = gtk_gesture_pan_set_property;

  drag_gesture_class->drag_update = gtk_gesture_pan_drag_update;
  drag_gesture_class->drag_end = gtk_gesture_pan_drag_end;

226
  /**
Matthias Clasen's avatar
Matthias Clasen committed
227
   * GtkGesturePan:orientation: (attributes org.gtk.Property.get=gtk_gesture_pan_get_orientation org.gtk.Property.set=gtk_gesture_pan_set_orientation)
228
229
230
   *
   * The expected orientation of pan gestures.
   */
Carlos Garnacho's avatar
Carlos Garnacho committed
231
232
233
234
235
  g_object_class_install_property (object_class,
                                   PROP_ORIENTATION,
                                   g_param_spec_enum ("orientation",
                                                      P_("Orientation"),
                                                      P_("Allowed orientations"),
236
237
                                                      GTK_TYPE_ORIENTATION,
                                                      GTK_ORIENTATION_HORIZONTAL,
238
239
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));

240
  /**
Carlos Garnacho's avatar
Carlos Garnacho committed
241
   * GtkGesturePan::pan:
242
243
244
245
   * @gesture: The object which received the signal
   * @direction: current direction of the pan gesture
   * @offset: Offset along the gesture orientation
   *
Matthias Clasen's avatar
Matthias Clasen committed
246
   * Emitted once a panning gesture along the expected axis is detected.
247
   */
Carlos Garnacho's avatar
Carlos Garnacho committed
248
  signals[PAN] =
249
    g_signal_new (I_("pan"),
Carlos Garnacho's avatar
Carlos Garnacho committed
250
251
252
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkGesturePanClass, pan),
253
254
                  NULL, NULL,
                  _gtk_marshal_VOID__ENUM_DOUBLE,
255
256
                  G_TYPE_NONE, 2, GTK_TYPE_PAN_DIRECTION,
                  G_TYPE_DOUBLE);
257
258
259
  g_signal_set_va_marshaller (signals[PAN],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__ENUM_DOUBLEv);
Carlos Garnacho's avatar
Carlos Garnacho committed
260
261
262
263
264
265
266
267
}

static void
gtk_gesture_pan_init (GtkGesturePan *gesture)
{
  GtkGesturePanPrivate *priv;

  priv = gtk_gesture_pan_get_instance_private (gesture);
268
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
Carlos Garnacho's avatar
Carlos Garnacho committed
269
270
}

271
272
273
274
/**
 * gtk_gesture_pan_new:
 * @orientation: expected orientation
 *
Matthias Clasen's avatar
Matthias Clasen committed
275
 * Returns a newly created `GtkGesture` that recognizes pan gestures.
276
 *
Matthias Clasen's avatar
Matthias Clasen committed
277
278
 * Returns: a newly created `GtkGesturePan`
 */
Carlos Garnacho's avatar
Carlos Garnacho committed
279
GtkGesture *
280
gtk_gesture_pan_new (GtkOrientation orientation)
Carlos Garnacho's avatar
Carlos Garnacho committed
281
282
283
284
285
286
{
  return g_object_new (GTK_TYPE_GESTURE_PAN,
                       "orientation", orientation,
                       NULL);
}

287
/**
Matthias Clasen's avatar
Matthias Clasen committed
288
289
 * gtk_gesture_pan_get_orientation: (attributes org.gtk.Method.get_property=orientation)
 * @gesture: A `GtkGesturePan`
290
291
292
 *
 * Returns the orientation of the pan gestures that this @gesture expects.
 *
293
 * Returns: the expected orientation for pan gestures
294
 */
295
GtkOrientation
Carlos Garnacho's avatar
Carlos Garnacho committed
296
297
298
299
300
301
302
303
304
305
306
gtk_gesture_pan_get_orientation (GtkGesturePan *gesture)
{
  GtkGesturePanPrivate *priv;

  g_return_val_if_fail (GTK_IS_GESTURE_PAN (gesture), 0);

  priv = gtk_gesture_pan_get_instance_private (gesture);

  return priv->orientation;
}

307
/**
Matthias Clasen's avatar
Matthias Clasen committed
308
309
 * gtk_gesture_pan_set_orientation: (attributes org.gtk.Method.set_property=orientation)
 * @gesture: A `GtkGesturePan`
310
311
312
313
 * @orientation: expected orientation
 *
 * Sets the orientation to be expected on pan gestures.
 */
Carlos Garnacho's avatar
Carlos Garnacho committed
314
void
315
316
gtk_gesture_pan_set_orientation (GtkGesturePan  *gesture,
                                 GtkOrientation  orientation)
Carlos Garnacho's avatar
Carlos Garnacho committed
317
318
319
320
{
  GtkGesturePanPrivate *priv;

  g_return_if_fail (GTK_IS_GESTURE_PAN (gesture));
321
322
  g_return_if_fail (orientation == GTK_ORIENTATION_HORIZONTAL ||
                    orientation == GTK_ORIENTATION_VERTICAL);
Carlos Garnacho's avatar
Carlos Garnacho committed
323
324
325
326
327
328
329
330
331

  priv = gtk_gesture_pan_get_instance_private (gesture);

  if (priv->orientation == orientation)
    return;

  priv->orientation = orientation;
  g_object_notify (G_OBJECT (gesture), "orientation");
}