adw-swipe-tracker.c 39.5 KB
Newer Older
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
1
2
3
/*
 * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
5
6
7
8
 */

#include "config.h"

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
9
10
#include "adw-swipe-tracker-private.h"
#include "adw-navigation-direction.h"
11
#include "adw-macros-private.h"
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
12
13
14

#include <math.h>

15
16
#define TOUCHPAD_BASE_DISTANCE_H 400
#define TOUCHPAD_BASE_DISTANCE_V 300
17
#define EVENT_HISTORY_THRESHOLD_MS 150
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
18
19
#define MIN_ANIMATION_DURATION 100
#define MAX_ANIMATION_DURATION 400
20
21
22
23
24
25
#define VELOCITY_THRESHOLD_TOUCH 0.3
#define VELOCITY_THRESHOLD_TOUCHPAD 0.6
#define DECELERATION_TOUCH 0.998
#define DECELERATION_TOUCHPAD 0.997
#define VELOCITY_CURVE_THRESHOLD 2
#define DECELERATION_PARABOLA_MULTIPLIER 0.35
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
26
27
#define DURATION_MULTIPLIER 3
#define ANIMATION_BASE_VELOCITY 0.002
28
#define DRAG_THRESHOLD_DISTANCE 16
29
30
#define EPSILON 0.005

31
32
33
34
35
36
#if GTK_CHECK_VERSION (4, 7, 0)
#define SCROLL_MULTIPLIER 1
#else
#define SCROLL_MULTIPLIER 10
#endif

37
#define SIGN(x) ((x) > 0.0 ? 1.0 : ((x) < 0.0 ? -1.0 : 0.0))
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
38
39

/**
40
 * AdwSwipeTracker:
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
41
 *
42
 * A swipe tracker used in [class@Carousel], [class@Flap] and [class@Leaflet].
43
44
 *
 * The `AdwSwipeTracker` object can be used for implementing widgets with swipe
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
45
46
47
 * gestures. It supports touch-based swipes, pointer dragging, and touchpad
 * scrolling.
 *
48
49
50
51
 * The widgets will probably want to expose the [property@SwipeTracker:enabled]
 * property. If they expect to use horizontal orientation,
 * [property@SwipeTracker:reversed] can be used for supporting RTL text
 * direction.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
52
 *
53
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
54
55
56
 */

typedef enum {
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
57
58
59
60
61
62
63
  ADW_SWIPE_TRACKER_STATE_NONE,
  ADW_SWIPE_TRACKER_STATE_PENDING,
  ADW_SWIPE_TRACKER_STATE_SCROLLING,
  ADW_SWIPE_TRACKER_STATE_FINISHING,
  ADW_SWIPE_TRACKER_STATE_REJECTED,
} AdwSwipeTrackerState;

64
typedef struct {
Christopher Davis's avatar
Christopher Davis committed
65
  double delta;
66
67
68
  guint32 time;
} EventHistoryRecord;

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
69
struct _AdwSwipeTracker
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
70
71
72
{
  GObject parent_instance;

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
73
  AdwSwipeable *swipeable;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
74
75
  gboolean enabled;
  gboolean reversed;
76
  gboolean allow_mouse_drag;
77
  gboolean allow_long_swipes;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
78
79
  GtkOrientation orientation;

Christopher Davis's avatar
Christopher Davis committed
80
81
  double pointer_x;
  double pointer_y;
82

83
84
  GArray *event_history;

Christopher Davis's avatar
Christopher Davis committed
85
86
  double initial_progress;
  double progress;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
87
88
  gboolean cancelled;

Christopher Davis's avatar
Christopher Davis committed
89
  double prev_offset;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
90

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
91
  AdwSwipeTrackerState state;
92
93
94

  GtkEventController *motion_controller;
  GtkEventController *scroll_controller;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
95
  GtkGesture *touch_gesture;
96
  GtkGesture *touch_gesture_capture;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
97
98
};

99
100
G_DEFINE_FINAL_TYPE_WITH_CODE (AdwSwipeTracker, adw_swipe_tracker, G_TYPE_OBJECT,
                               G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
101
102
103

enum {
  PROP_0,
104
  PROP_SWIPEABLE,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
105
106
  PROP_ENABLED,
  PROP_REVERSED,
107
  PROP_ALLOW_MOUSE_DRAG,
108
  PROP_ALLOW_LONG_SWIPES,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
109
110
111

  /* GtkOrientable */
  PROP_ORIENTATION,
112
  LAST_PROP = PROP_ALLOW_LONG_SWIPES + 1,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
113
114
115
116
};

static GParamSpec *props[LAST_PROP];

117
enum {
118
  SIGNAL_PREPARE,
119
  SIGNAL_BEGIN_SWIPE,
120
  SIGNAL_UPDATE_SWIPE,
121
  SIGNAL_END_SWIPE,
122
123
124
125
126
  SIGNAL_LAST_SIGNAL,
};

static guint signals[SIGNAL_LAST_SIGNAL];

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
static void
swipeable_notify_cb (AdwSwipeTracker *self)
{
  self->motion_controller = NULL;
  self->scroll_controller = NULL;
  self->touch_gesture = NULL;
  self->touch_gesture_capture = NULL;
  self->swipeable = NULL;
}

static void
set_swipeable (AdwSwipeTracker *self,
               AdwSwipeable    *swipeable)
{
  if (self->swipeable == swipeable)
    return;

  if (self->swipeable)
    g_object_weak_unref (G_OBJECT (self->swipeable),
                         (GWeakNotify) swipeable_notify_cb,
                         self);

  self->swipeable = swipeable;

  if (self->swipeable)
    g_object_weak_ref (G_OBJECT (self->swipeable),
                       (GWeakNotify) swipeable_notify_cb,
                       self);
}

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
157
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
158
reset (AdwSwipeTracker *self)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
159
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
160
  self->state = ADW_SWIPE_TRACKER_STATE_NONE;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
161
162
163
164
165
166

  self->prev_offset = 0;

  self->initial_progress = 0;
  self->progress = 0;

167
168
  g_array_remove_range (self->event_history, 0, self->event_history->len);

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
169
170
171
  self->cancelled = FALSE;
}

172
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
173
get_range (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
174
175
           double          *first,
           double          *last)
176
{
Chun-wei Fan's avatar
Chun-wei Fan committed
177
  double *points;
Christopher Davis's avatar
Christopher Davis committed
178
  int n;
179

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
180
  points = adw_swipeable_get_snap_points (self->swipeable, &n);
181
182
183

  *first = points[0];
  *last = points[n - 1];
Chun-wei Fan's avatar
Chun-wei Fan committed
184
185

  g_free (points);
186
187
}

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
188
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
189
gesture_prepare (AdwSwipeTracker        *self,
190
                 AdwNavigationDirection  direction)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
191
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
192
  if (self->state != ADW_SWIPE_TRACKER_STATE_NONE)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
193
194
    return;

195
  g_signal_emit (self, signals[SIGNAL_PREPARE], 0, direction);
196

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
197
  self->initial_progress = adw_swipeable_get_progress (self->swipeable);
198
  self->progress = self->initial_progress;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
199
  self->state = ADW_SWIPE_TRACKER_STATE_PENDING;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
200
201
202
}

static void
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
trim_history (AdwSwipeTracker *self,
              guint32          current_time)
{
  guint32 threshold_time = current_time - EVENT_HISTORY_THRESHOLD_MS;
  guint i;

  for (i = 0; i < self->event_history->len; i++) {
    guint32 time = g_array_index (self->event_history,
                                  EventHistoryRecord, i).time;

    if (time >= threshold_time)
      break;
  }

  if (i > 0)
    g_array_remove_range (self->event_history, 0, i);
}

static void
append_to_history (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
223
                   double           delta,
224
                   guint32          time)
225
226
227
228
229
230
231
232
233
234
235
{
  EventHistoryRecord record;

  trim_history (self, time);

  record.delta = delta;
  record.time = time;

  g_array_append_val (self->event_history, record);
}

Christopher Davis's avatar
Christopher Davis committed
236
static double
237
238
calculate_velocity (AdwSwipeTracker *self)
{
Christopher Davis's avatar
Christopher Davis committed
239
  double total_delta = 0;
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  guint32 first_time = 0, last_time = 0;
  guint i;

  for (i = 0; i < self->event_history->len; i++) {
    EventHistoryRecord *r =
      &g_array_index (self->event_history, EventHistoryRecord, i);

    if (i == 0)
      first_time = r->time;
    else
      total_delta += r->delta;

    last_time = r->time;
  }

  if (first_time == last_time)
    return 0;

  return total_delta / (last_time - first_time);
}

static void
gesture_begin (AdwSwipeTracker *self)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
263
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
264
  if (self->state != ADW_SWIPE_TRACKER_STATE_PENDING)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
265
266
    return;

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
267
  self->state = ADW_SWIPE_TRACKER_STATE_SCROLLING;
268
269

  g_signal_emit (self, signals[SIGNAL_BEGIN_SWIPE], 0);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
270
271
}

Christopher Davis's avatar
Christopher Davis committed
272
static int
Christopher Davis's avatar
Christopher Davis committed
273
274
275
find_closest_point (double *points,
                    int     n,
                    double  pos)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
276
{
277
  guint i, min = 0;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
278

279
280
281
  for (i = 1; i < n; i++)
    if (ABS (points[i] - pos) < ABS (points[min] - pos))
      min = i;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
282

283
284
  return min;
}
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
285

Christopher Davis's avatar
Christopher Davis committed
286
static int
Christopher Davis's avatar
Christopher Davis committed
287
288
289
find_next_point (double *points,
                 int     n,
                 double  pos)
290
291
{
  guint i;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
292

293
294
295
  for (i = 0; i < n; i++)
    if (points[i] >= pos)
      return i;
296

297
298
  return -1;
}
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
299

Christopher Davis's avatar
Christopher Davis committed
300
static int
Christopher Davis's avatar
Christopher Davis committed
301
302
303
find_previous_point (double *points,
                     int     n,
                     double  pos)
304
{
Christopher Davis's avatar
Christopher Davis committed
305
  int i;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
306

307
308
309
  for (i = n - 1; i >= 0; i--)
    if (points[i] <= pos)
      return i;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
310

311
312
313
  return -1;
}

Christopher Davis's avatar
Christopher Davis committed
314
static int
315
find_point_for_projection (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
316
                           double          *points,
Christopher Davis's avatar
Christopher Davis committed
317
                           int              n,
Christopher Davis's avatar
Christopher Davis committed
318
319
                           double           pos,
                           double           velocity)
320
{
Christopher Davis's avatar
Christopher Davis committed
321
322
323
  int initial = find_closest_point (points, n, self->initial_progress);
  int prev = find_previous_point (points, n, pos);
  int next = find_next_point (points, n, pos);
324
325
326
327
328

  if ((velocity > 0 ? prev : next) == initial)
    return velocity > 0 ? next : prev;

  return find_closest_point (points, n, pos);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
329
330
331
}

static void
332
get_bounds (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
333
            double          *points,
Christopher Davis's avatar
Christopher Davis committed
334
            int              n,
Christopher Davis's avatar
Christopher Davis committed
335
336
337
            double           pos,
            double          *lower,
            double          *upper)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
338
{
Christopher Davis's avatar
Christopher Davis committed
339
340
  int prev, next;
  int closest = find_closest_point (points, n, pos);
341

342
  if (ABS (points[closest] - pos) < EPSILON) {
343
344
    prev = next = closest;
  } else {
345
346
    prev = find_previous_point (points, n, pos);
    next = find_next_point (points, n, pos);
347
  }
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
348

349
350
351
352
353
354
  *lower = points[MAX (prev - 1, 0)];
  *upper = points[MIN (next + 1, n - 1)];
}

static void
gesture_update (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
355
                double           delta,
356
357
                guint32          time)
{
Christopher Davis's avatar
Christopher Davis committed
358
359
  double lower, upper;
  double progress;
360
361
362

  if (self->state != ADW_SWIPE_TRACKER_STATE_SCROLLING)
    return;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
363

364
  if (!self->allow_long_swipes) {
Chun-wei Fan's avatar
Chun-wei Fan committed
365
    double *points;
Christopher Davis's avatar
Christopher Davis committed
366
    int n;
367
368
369

    points = adw_swipeable_get_snap_points (self->swipeable, &n);
    get_bounds (self, points, n, self->initial_progress, &lower, &upper);
Chun-wei Fan's avatar
Chun-wei Fan committed
370
371

    g_free (points);
372
373
374
  } else {
    get_range (self, &lower, &upper);
  }
375

376
377
  progress = self->progress + delta;
  progress = CLAMP (progress, lower, upper);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
378

379
  self->progress = progress;
380

381
  g_signal_emit (self, signals[SIGNAL_UPDATE_SWIPE], 0, progress);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
382
383
}

Christopher Davis's avatar
Christopher Davis committed
384
static double
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
385
get_end_progress (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
386
                  double           velocity,
387
                  gboolean         is_touchpad)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
388
{
Christopher Davis's avatar
Christopher Davis committed
389
  double pos, decel, slope;
Chun-wei Fan's avatar
Chun-wei Fan committed
390
  double *points;
Christopher Davis's avatar
Christopher Davis committed
391
  int n;
392
  double lower, upper;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
393
394

  if (self->cancelled)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
395
    return adw_swipeable_get_cancel_progress (self->swipeable);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
396

397
398
  points = adw_swipeable_get_snap_points (self->swipeable, &n);

Chun-wei Fan's avatar
Chun-wei Fan committed
399
400
401
402
403
404
405
  if (ABS (velocity) < (is_touchpad ? VELOCITY_THRESHOLD_TOUCHPAD : VELOCITY_THRESHOLD_TOUCH)) {
    pos = points[find_closest_point (points, n, self->progress)];

    g_free (points);

    return pos;
  }
406
407
408
409
410

  decel = is_touchpad ? DECELERATION_TOUCHPAD : DECELERATION_TOUCH;
  slope = decel / (1.0 - decel) / 1000.0;

  if (ABS (velocity) > VELOCITY_CURVE_THRESHOLD) {
Christopher Davis's avatar
Christopher Davis committed
411
412
    const double c = slope / 2 / DECELERATION_PARABOLA_MULTIPLIER;
    const double x = ABS (velocity) - VELOCITY_CURVE_THRESHOLD + c;
413
414
415
416
417
418
419
420
421
422

    pos = DECELERATION_PARABOLA_MULTIPLIER * x * x
        - DECELERATION_PARABOLA_MULTIPLIER * c * c
        + slope * VELOCITY_CURVE_THRESHOLD;
  } else {
    pos = ABS (velocity) * slope;
  }

  pos = (pos * SIGN (velocity)) + self->progress;

Chun-wei Fan's avatar
Chun-wei Fan committed
423
  if (!self->allow_long_swipes)
424
    get_bounds (self, points, n, self->initial_progress, &lower, &upper);
Chun-wei Fan's avatar
Chun-wei Fan committed
425
  else
426
    get_range (self, &lower, &upper);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
427

428
  pos = CLAMP (pos, lower, upper);
429
  pos = points[find_point_for_projection (self, points, n, pos, velocity)];
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
430

Chun-wei Fan's avatar
Chun-wei Fan committed
431
432
  g_free (points);

433
  return pos;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
434
435
436
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
437
gesture_end (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
438
             double           distance,
439
440
             guint32          time,
             gboolean         is_touchpad)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
441
{
Christopher Davis's avatar
Christopher Davis committed
442
  double end_progress, velocity;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
443

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
444
  if (self->state == ADW_SWIPE_TRACKER_STATE_NONE)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
445
446
    return;

447
448
449
  trim_history (self, time);

  velocity = calculate_velocity (self);
450
451
  end_progress = get_end_progress (self, velocity, is_touchpad);

452
  g_signal_emit (self, signals[SIGNAL_END_SWIPE], 0, velocity, end_progress);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
453

454
  if (!self->cancelled)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
455
    self->state = ADW_SWIPE_TRACKER_STATE_FINISHING;
456
457

  reset (self);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
458
459
460
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
461
gesture_cancel (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
462
                double           distance,
463
464
                guint32          time,
                gboolean         is_touchpad)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
465
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
466
467
  if (self->state != ADW_SWIPE_TRACKER_STATE_PENDING &&
      self->state != ADW_SWIPE_TRACKER_STATE_SCROLLING) {
468
469
    reset (self);

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
470
    return;
471
  }
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
472
473

  self->cancelled = TRUE;
474
  gesture_end (self, distance, time, is_touchpad);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
475
476
}

477
static gboolean
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
478
should_suppress_drag (AdwSwipeTracker *self,
479
480
481
482
483
484
485
486
487
488
489
490
491
492
                      GtkWidget       *widget)
{
  GtkWidget *parent = widget;
  gboolean found_window_handle = FALSE;

  while (parent && parent != GTK_WIDGET (self->swipeable)) {
    found_window_handle |= GTK_IS_WINDOW_HANDLE (parent);

    parent = gtk_widget_get_parent (parent);
  }

  return found_window_handle;
}

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508

static inline gboolean
is_in_swipe_area (AdwSwipeTracker        *self,
                  double                  x,
                  double                  y,
                  AdwNavigationDirection  direction,
                  gboolean                is_drag)
{
  GdkRectangle rect;

  adw_swipeable_get_swipe_area (self->swipeable, direction, is_drag, &rect);

  return x >= rect.x && x < rect.x + rect.width &&
         y >= rect.y && y < rect.y + rect.height;
}

509
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
510
drag_capture_begin_cb (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
511
512
                       double           start_x,
                       double           start_y,
513
514
                       GtkGestureDrag  *gesture)
{
515
  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
516
517
}

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
518
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
519
drag_begin_cb (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
520
521
               double           start_x,
               double           start_y,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
522
523
               GtkGestureDrag  *gesture)
{
524
525
  GtkWidget *widget;

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
526
  if (self->state != ADW_SWIPE_TRACKER_STATE_NONE) {
527
528
529
530
531
532
533
534
535
536
537
538
539
    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
    return;
  }

  widget = gtk_widget_pick (GTK_WIDGET (self->swipeable),
                          start_x,
                          start_y,
                          GTK_PICK_DEFAULT);

  if (should_suppress_drag (self, widget)) {
    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
    return;
  }
540

541
  gtk_gesture_set_state (self->touch_gesture_capture, GTK_EVENT_SEQUENCE_DENIED);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
542
543
544
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
545
drag_update_cb (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
546
547
                double           offset_x,
                double           offset_y,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
548
549
                GtkGestureDrag  *gesture)
{
Christopher Davis's avatar
Christopher Davis committed
550
  double offset, distance, delta;
551
  gboolean is_vertical, is_offset_vertical;
552
  guint32 time;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
553

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
554
  distance = adw_swipeable_get_distance (self->swipeable);
555

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
556
  is_vertical = (self->orientation == GTK_ORIENTATION_VERTICAL);
557
  offset = is_vertical ? offset_y : offset_x;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
558

559
  if (!self->reversed)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
560
561
    offset = -offset;

562
563
564
  delta = offset - self->prev_offset;
  self->prev_offset = offset;

565
566
  is_offset_vertical = (ABS (offset_y) > ABS (offset_x));

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
567
  if (self->state == ADW_SWIPE_TRACKER_STATE_REJECTED) {
568
    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
569
570
571
    return;
  }

572
573
574
575
  time = gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (gesture));

  append_to_history (self, delta, time);

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
576
  if (self->state == ADW_SWIPE_TRACKER_STATE_NONE) {
577
    if (is_vertical == is_offset_vertical)
578
      gesture_prepare (self, offset > 0 ? ADW_NAVIGATION_DIRECTION_FORWARD : ADW_NAVIGATION_DIRECTION_BACK);
579
    else
580
      gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
581
582
583
    return;
  }

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
584
  if (self->state == ADW_SWIPE_TRACKER_STATE_PENDING) {
Christopher Davis's avatar
Christopher Davis committed
585
586
    double drag_distance;
    double first_point, last_point;
587
    gboolean is_overshooting;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
588

589
    get_range (self, &first_point, &last_point);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
590

591
    drag_distance = sqrt (offset_x * offset_x + offset_y * offset_y);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
592
593
594
    is_overshooting = (offset < 0 && self->progress <= first_point) ||
                      (offset > 0 && self->progress >= last_point);

595
    if (drag_distance >= DRAG_THRESHOLD_DISTANCE) {
596
597
598
599
600
601
602
603
604
605
      double start_x, start_y;
      AdwNavigationDirection direction;

      gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
      direction = offset > 0 ? ADW_NAVIGATION_DIRECTION_FORWARD : ADW_NAVIGATION_DIRECTION_BACK;

      if (!is_in_swipe_area (self, start_x, start_y, direction, TRUE) &&
          !is_in_swipe_area (self, start_x + offset_x, start_y + offset_y, direction, TRUE))
        return;

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
606
      if ((is_vertical == is_offset_vertical) && !is_overshooting) {
607
        gesture_begin (self);
608
        self->prev_offset = offset;
609
        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
610
      } else {
611
        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
612
613
614
615
      }
    }
  }

616
617
  if (self->state == ADW_SWIPE_TRACKER_STATE_SCROLLING)
    gesture_update (self, delta / distance, time);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
618
619
620
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
621
drag_end_cb (AdwSwipeTracker *self,
Christopher Davis's avatar
Christopher Davis committed
622
623
             double           offset_x,
             double           offset_y,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
624
625
             GtkGestureDrag  *gesture)
{
Christopher Davis's avatar
Christopher Davis committed
626
  double distance;
627
  guint32 time;
628

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
629
  distance = adw_swipeable_get_distance (self->swipeable);
630

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
631
  if (self->state == ADW_SWIPE_TRACKER_STATE_REJECTED) {
632
633
634
635
636
637
    gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);

    reset (self);
    return;
  }

638
639
  time = gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (gesture));

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
640
  if (self->state != ADW_SWIPE_TRACKER_STATE_SCROLLING) {
641
    gesture_cancel (self, distance, time, FALSE);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
642
643
644
645
    gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
    return;
  }

646
  gesture_end (self, distance, time, FALSE);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
647
648
649
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
650
drag_cancel_cb (AdwSwipeTracker  *self,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
651
652
653
                GdkEventSequence *sequence,
                GtkGesture       *gesture)
{
654
  guint32 time;
Christopher Davis's avatar
Christopher Davis committed
655
  double distance;
656

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
657
  distance = adw_swipeable_get_distance (self->swipeable);
658

659
660
  time = gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (gesture));

661
  gesture_cancel (self, distance, time, FALSE);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
662
663
664
665
  gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
}

static gboolean
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
666
handle_scroll_event (AdwSwipeTracker *self,
667
                     GdkEvent        *event)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
668
669
670
{
  GdkDevice *source_device;
  GdkInputSource input_source;
Christopher Davis's avatar
Christopher Davis committed
671
  double dx, dy, delta, distance;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
672
  gboolean is_vertical;
673
  guint32 time;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
674

675
676
677
  is_vertical = (self->orientation == GTK_ORIENTATION_VERTICAL);
  distance = is_vertical ? TOUCHPAD_BASE_DISTANCE_V : TOUCHPAD_BASE_DISTANCE_H;

678
679
680
  if (!event || gdk_event_get_event_type (event) != GDK_SCROLL)
    return GDK_EVENT_PROPAGATE;

681
  if (gdk_scroll_event_get_direction (event) != GDK_SCROLL_SMOOTH)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
682
683
    return GDK_EVENT_PROPAGATE;

684
  source_device = gdk_event_get_device (event);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
685
  input_source = gdk_device_get_source (source_device);
686
  if (input_source != GDK_SOURCE_TOUCHPAD)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
687
688
    return GDK_EVENT_PROPAGATE;

689
  gdk_scroll_event_get_deltas (event, &dx, &dy);
690
691
692
  delta = is_vertical ? dy : dx;
  if (self->reversed)
    delta = -delta;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
693

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
694
  if (self->state == ADW_SWIPE_TRACKER_STATE_REJECTED) {
695
    if (gdk_scroll_event_is_stop (event))
696
697
698
699
700
      reset (self);

    return GDK_EVENT_PROPAGATE;
  }

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
701
  if (self->state == ADW_SWIPE_TRACKER_STATE_NONE) {
702
703
    AdwNavigationDirection direction;

704
    if (gdk_scroll_event_is_stop (event))
705
      return GDK_EVENT_PROPAGATE;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
706

707
708
709
710
711
712
    direction = delta > 0 ? ADW_NAVIGATION_DIRECTION_FORWARD : ADW_NAVIGATION_DIRECTION_BACK;

    if (!is_in_swipe_area (self, self->pointer_x, self->pointer_y, direction, FALSE)) {
      self->state = ADW_SWIPE_TRACKER_STATE_REJECTED;
      return GDK_EVENT_PROPAGATE;
    }
713

714
    gesture_prepare (self, delta > 0 ? ADW_NAVIGATION_DIRECTION_FORWARD : ADW_NAVIGATION_DIRECTION_BACK);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
715
716
  }

717
718
  time = gdk_event_get_time (event);

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
719
  if (self->state == ADW_SWIPE_TRACKER_STATE_PENDING) {
720
    gboolean is_overshooting;
Christopher Davis's avatar
Christopher Davis committed
721
    double first_point, last_point;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
722

723
    get_range (self, &first_point, &last_point);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
724
725
726
727

    is_overshooting = (delta < 0 && self->progress <= first_point) ||
                      (delta > 0 && self->progress >= last_point);

728
729
    append_to_history (self, delta * SCROLL_MULTIPLIER, time);

730
    if (!is_overshooting)
731
      gesture_begin (self);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
732
    else
733
      gesture_cancel (self, distance, time, TRUE);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
734
735
  }

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
736
  if (self->state == ADW_SWIPE_TRACKER_STATE_SCROLLING) {
737
    if (gdk_scroll_event_is_stop (event)) {
738
      gesture_end (self, distance, time, TRUE);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
739
    } else {
740
741
      append_to_history (self, delta * SCROLL_MULTIPLIER, time);

742
      gesture_update (self, delta / distance * SCROLL_MULTIPLIER, time);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
743
744
745
746
      return GDK_EVENT_STOP;
    }
  }

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
747
  if (self->state == ADW_SWIPE_TRACKER_STATE_FINISHING)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
748
749
750
751
752
    reset (self);

  return GDK_EVENT_PROPAGATE;
}

753
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
754
scroll_begin_cb (AdwSwipeTracker          *self,
755
                 GtkEventControllerScroll *controller)
756
{
757
  GdkEvent *event;
758

759
  event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (controller));
760

761
  handle_scroll_event (self, event);
762
763
}

764
static gboolean
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
765
scroll_cb (AdwSwipeTracker          *self,
Christopher Davis's avatar
Christopher Davis committed
766
767
           double                    dx,
           double                    dy,
768
           GtkEventControllerScroll *controller)
769
{
770
  GdkEvent *event;
771

772
  event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (controller));
773

774
  return handle_scroll_event (self, event);
775
776
}

777
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
778
scroll_end_cb (AdwSwipeTracker          *self,
779
               GtkEventControllerScroll *controller)
780
{
781
  GdkEvent *event;
782

783
  event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (controller));
784

785
  handle_scroll_event (self, event);
786
787
}

788
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
789
motion_cb (AdwSwipeTracker          *self,
Christopher Davis's avatar
Christopher Davis committed
790
791
           double                    x,
           double                    y,
792
           GtkEventControllerMotion *controller)
793
{
794
795
  self->pointer_x = x;
  self->pointer_y = y;
796
797
}

798
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
799
update_controllers (AdwSwipeTracker *self)
800
{
801
  GtkEventControllerScrollFlags flags;
802

803
804
805
806
  if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
    flags = GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL;
  else
    flags = GTK_EVENT_CONTROLLER_SCROLL_VERTICAL;
807

808
809
810
811
812
  if (self->scroll_controller) {
    gtk_event_controller_scroll_set_flags (GTK_EVENT_CONTROLLER_SCROLL (self->scroll_controller), flags);
    gtk_event_controller_set_propagation_phase (self->scroll_controller,
                                                self->enabled ? GTK_PHASE_BUBBLE : GTK_PHASE_NONE);
  }
813

814
815
816
  if (self->motion_controller)
    gtk_event_controller_set_propagation_phase (self->motion_controller,
                                                self->enabled ? GTK_PHASE_CAPTURE : GTK_PHASE_NONE);
817

818
819
820
  if (self->touch_gesture)
    gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->touch_gesture),
                                                self->enabled ? GTK_PHASE_BUBBLE : GTK_PHASE_NONE);
821

822
823
824
825
  if (self->touch_gesture_capture)
    gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->touch_gesture_capture),
                                                self->enabled ? GTK_PHASE_CAPTURE : GTK_PHASE_NONE);
}
826

827
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
828
set_orientation (AdwSwipeTracker *self,
829
830
                 GtkOrientation   orientation)
{
831

832
833
  if (orientation == self->orientation)
    return;
834

835
836
  self->orientation = orientation;
  update_controllers (self);
837

838
  g_object_notify (G_OBJECT (self), "orientation");
839
840
}

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
841
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
842
adw_swipe_tracker_constructed (GObject *object)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
843
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
844
  AdwSwipeTracker *self = ADW_SWIPE_TRACKER (object);
845
  GtkEventController *controller;
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
846

847
  g_assert (self->swipeable);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
848

849
  g_signal_connect_object (self->swipeable, "unrealize", G_CALLBACK (reset), self, G_CONNECT_SWAPPED);
850

851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
  controller = gtk_event_controller_motion_new ();
  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
  g_signal_connect_object (controller, "motion", G_CALLBACK (motion_cb), self, G_CONNECT_SWAPPED);
  gtk_widget_add_controller (GTK_WIDGET (self->swipeable), controller);
  self->motion_controller = controller;

  controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
  g_signal_connect_object (controller, "drag-begin", G_CALLBACK (drag_capture_begin_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "drag-update", G_CALLBACK (drag_update_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "drag-end", G_CALLBACK (drag_end_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "cancel", G_CALLBACK (drag_cancel_cb), self, G_CONNECT_SWAPPED);
  gtk_widget_add_controller (GTK_WIDGET (self->swipeable), controller);
  self->touch_gesture_capture = GTK_GESTURE (controller);

  controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
  g_signal_connect_object (controller, "drag-begin", G_CALLBACK (drag_begin_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "drag-update", G_CALLBACK (drag_update_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "drag-end", G_CALLBACK (drag_end_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "cancel", G_CALLBACK (drag_cancel_cb), self, G_CONNECT_SWAPPED);
  gtk_widget_add_controller (GTK_WIDGET (self->swipeable), controller);
  self->touch_gesture = GTK_GESTURE (controller);

  g_object_bind_property (self, "allow-mouse-drag",
                          self->touch_gesture, "touch-only",
                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);

  g_object_bind_property (self, "allow-mouse-drag",
                          self->touch_gesture_capture, "touch-only",
                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);

  controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_NONE);
  g_signal_connect_object (controller, "scroll-begin", G_CALLBACK (scroll_begin_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "scroll", G_CALLBACK (scroll_cb), self, G_CONNECT_SWAPPED);
  g_signal_connect_object (controller, "scroll-end", G_CALLBACK (scroll_end_cb), self, G_CONNECT_SWAPPED);
  gtk_widget_add_controller (GTK_WIDGET (self->swipeable), controller);
  self->scroll_controller = controller;

  update_controllers (self);
889

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
890
  G_OBJECT_CLASS (adw_swipe_tracker_parent_class)->constructed (object);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
891
892
893
}

static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
894
adw_swipe_tracker_dispose (GObject *object)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
895
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
896
  AdwSwipeTracker *self = ADW_SWIPE_TRACKER (object);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
897

898
899
900
  if (self->touch_gesture) {
    gtk_widget_remove_controller (GTK_WIDGET (self->swipeable),
                                  GTK_EVENT_CONTROLLER (self->touch_gesture));
901
902
903
904
905
906
907
    self->touch_gesture = NULL;
  }

  if (self->touch_gesture_capture) {
    gtk_widget_remove_controller (GTK_WIDGET (self->swipeable),
                                  GTK_EVENT_CONTROLLER (self->touch_gesture_capture));
    self->touch_gesture_capture = NULL;
908
  }
909

910
911
  if (self->motion_controller) {
    gtk_widget_remove_controller (GTK_WIDGET (self->swipeable), self->motion_controller);
912
    self->motion_controller = NULL;
913
  }
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
914

915
916
  if (self->scroll_controller) {
    gtk_widget_remove_controller (GTK_WIDGET (self->swipeable), self->scroll_controller);
917
    self->scroll_controller = NULL;
918
  }
919

920
  set_swipeable (self, NULL);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
921

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
922
  G_OBJECT_CLASS (adw_swipe_tracker_parent_class)->dispose (object);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
923
924
}

bob's avatar
bob committed
925
926
927
928
929
930
931
932
933
934
static void
adw_swipe_tracker_finalize (GObject *object)
{
  AdwSwipeTracker *self = ADW_SWIPE_TRACKER (object);

  g_array_free (self->event_history, TRUE);

  G_OBJECT_CLASS (adw_swipe_tracker_parent_class)->finalize (object);
}

Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
935
static void
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
936
adw_swipe_tracker_get_property (GObject    *object,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
937
938
939
940
                                guint       prop_id,
                                GValue     *value,
                                GParamSpec *pspec)
{
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
941
  AdwSwipeTracker *