1718 lines
50 KiB
Diff
1718 lines
50 KiB
Diff
|
From: Alexander Mikhaylenko <alexm@gnome.org>
|
||
|
Date: Wed, 14 Oct 2020 17:10:54 +0500
|
||
|
Subject: Add GtkHdySwipeable and GtkHdySwipeTracker
|
||
|
|
||
|
This is imported from HdySwipeable and HdySwipeTracker from libhandy 1.0.2.
|
||
|
---
|
||
|
gtk/hdy-swipe-tracker-private.h | 52 ++
|
||
|
gtk/hdy-swipe-tracker.c | 1246 +++++++++++++++++++++++++++++++++++++++
|
||
|
gtk/hdy-swipeable-private.h | 82 +++
|
||
|
gtk/hdy-swipeable.c | 274 +++++++++
|
||
|
gtk/meson.build | 4 +
|
||
|
5 files changed, 1658 insertions(+)
|
||
|
create mode 100644 gtk/hdy-swipe-tracker-private.h
|
||
|
create mode 100644 gtk/hdy-swipe-tracker.c
|
||
|
create mode 100644 gtk/hdy-swipeable-private.h
|
||
|
create mode 100644 gtk/hdy-swipeable.c
|
||
|
|
||
|
diff --git a/gtk/hdy-swipe-tracker-private.h b/gtk/hdy-swipe-tracker-private.h
|
||
|
new file mode 100644
|
||
|
index 0000000..c886734
|
||
|
--- /dev/null
|
||
|
+++ b/gtk/hdy-swipe-tracker-private.h
|
||
|
@@ -0,0 +1,52 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
|
||
|
+ *
|
||
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
||
|
+ */
|
||
|
+
|
||
|
+#pragma once
|
||
|
+
|
||
|
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||
|
+#error "Only <gtk/gtk.h> can be included directly."
|
||
|
+#endif
|
||
|
+
|
||
|
+#include "hdy-navigation-direction-private.h"
|
||
|
+#include "hdy-swipeable-private.h"
|
||
|
+
|
||
|
+G_BEGIN_DECLS
|
||
|
+
|
||
|
+#define GTK_HDY_SWIPE_BORDER 32
|
||
|
+
|
||
|
+#define GTK_TYPE_HDY_SWIPE_TRACKER (gtk_hdy_swipe_tracker_get_type())
|
||
|
+
|
||
|
+G_DECLARE_FINAL_TYPE (GtkHdySwipeTracker, gtk_hdy_swipe_tracker, GTK, HDY_SWIPE_TRACKER, GObject)
|
||
|
+
|
||
|
+GtkHdySwipeTracker *gtk_hdy_swipe_tracker_new (GtkHdySwipeable *swipeable);
|
||
|
+
|
||
|
+GtkHdySwipeable *gtk_hdy_swipe_tracker_get_swipeable (GtkHdySwipeTracker *self);
|
||
|
+
|
||
|
+gboolean gtk_hdy_swipe_tracker_get_enabled (GtkHdySwipeTracker *self);
|
||
|
+void gtk_hdy_swipe_tracker_set_enabled (GtkHdySwipeTracker *self,
|
||
|
+ gboolean enabled);
|
||
|
+
|
||
|
+gboolean gtk_hdy_swipe_tracker_get_reversed (GtkHdySwipeTracker *self);
|
||
|
+void gtk_hdy_swipe_tracker_set_reversed (GtkHdySwipeTracker *self,
|
||
|
+ gboolean reversed);
|
||
|
+
|
||
|
+gboolean gtk_hdy_swipe_tracker_get_allow_mouse_drag (GtkHdySwipeTracker *self);
|
||
|
+void gtk_hdy_swipe_tracker_set_allow_mouse_drag (GtkHdySwipeTracker *self,
|
||
|
+ gboolean allow_mouse_drag);
|
||
|
+
|
||
|
+void gtk_hdy_swipe_tracker_shift_position (GtkHdySwipeTracker *self,
|
||
|
+ gdouble delta);
|
||
|
+
|
||
|
+void gtk_hdy_swipe_tracker_emit_begin_swipe (GtkHdySwipeTracker *self,
|
||
|
+ GtkHdyNavigationDirection direction,
|
||
|
+ gboolean direct);
|
||
|
+void gtk_hdy_swipe_tracker_emit_update_swipe (GtkHdySwipeTracker *self,
|
||
|
+ gdouble progress);
|
||
|
+void gtk_hdy_swipe_tracker_emit_end_swipe (GtkHdySwipeTracker *self,
|
||
|
+ gint64 duration,
|
||
|
+ gdouble to);
|
||
|
+
|
||
|
+G_END_DECLS
|
||
|
diff --git a/gtk/hdy-swipe-tracker.c b/gtk/hdy-swipe-tracker.c
|
||
|
new file mode 100644
|
||
|
index 0000000..9c8161b
|
||
|
--- /dev/null
|
||
|
+++ b/gtk/hdy-swipe-tracker.c
|
||
|
@@ -0,0 +1,1246 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
|
||
|
+ *
|
||
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
||
|
+ */
|
||
|
+
|
||
|
+#include "config.h"
|
||
|
+#include <glib/gi18n-lib.h>
|
||
|
+
|
||
|
+#include "hdy-swipe-tracker-private.h"
|
||
|
+
|
||
|
+#include <gdk/gdk.h>
|
||
|
+#include "gtkbutton.h"
|
||
|
+#include "gtkgesture.h"
|
||
|
+#include "gtkgesturedrag.h"
|
||
|
+#include "gtkmain.h"
|
||
|
+#include "gtkorientable.h"
|
||
|
+#include "gtktypebuiltins.h"
|
||
|
+#include "gtkprivatetypebuiltins.h"
|
||
|
+#include "gtkwidget.h"
|
||
|
+#include "gtkwindow.h"
|
||
|
+
|
||
|
+#include <math.h>
|
||
|
+
|
||
|
+#define TOUCHPAD_BASE_DISTANCE_H 400
|
||
|
+#define TOUCHPAD_BASE_DISTANCE_V 300
|
||
|
+#define SCROLL_MULTIPLIER 10
|
||
|
+#define MIN_ANIMATION_DURATION 100
|
||
|
+#define MAX_ANIMATION_DURATION 400
|
||
|
+#define VELOCITY_THRESHOLD 0.4
|
||
|
+#define DURATION_MULTIPLIER 3
|
||
|
+#define ANIMATION_BASE_VELOCITY 0.002
|
||
|
+#define DRAG_THRESHOLD_DISTANCE 16
|
||
|
+
|
||
|
+/**
|
||
|
+ * SECTION:hdy-swipe-tracker
|
||
|
+ * @short_description: Swipe tracker used in #GtkHdyCarousel and #GtkHdyLeaflet
|
||
|
+ * @title: GtkHdySwipeTracker
|
||
|
+ * @See_also: #GtkHdyCarousel, #GtkHdyDeck, #GtkHdyLeaflet, #GtkHdySwipeable
|
||
|
+ *
|
||
|
+ * The GtkHdySwipeTracker object can be used for implementing widgets with swipe
|
||
|
+ * gestures. It supports touch-based swipes, pointer dragging, and touchpad
|
||
|
+ * scrolling.
|
||
|
+ *
|
||
|
+ * The widgets will probably want to expose #GtkHdySwipeTracker:enabled property.
|
||
|
+ * If they expect to use horizontal orientation, #GtkHdySwipeTracker:reversed
|
||
|
+ * property can be used for supporting RTL text direction.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+
|
||
|
+typedef enum {
|
||
|
+ GTK_HDY_SWIPE_TRACKER_STATE_NONE,
|
||
|
+ GTK_HDY_SWIPE_TRACKER_STATE_PENDING,
|
||
|
+ GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING,
|
||
|
+ GTK_HDY_SWIPE_TRACKER_STATE_FINISHING,
|
||
|
+ GTK_HDY_SWIPE_TRACKER_STATE_REJECTED,
|
||
|
+} GtkHdySwipeTrackerState;
|
||
|
+
|
||
|
+struct _GtkHdySwipeTracker
|
||
|
+{
|
||
|
+ GObject parent_instance;
|
||
|
+
|
||
|
+ GtkHdySwipeable *swipeable;
|
||
|
+ gboolean enabled;
|
||
|
+ gboolean reversed;
|
||
|
+ gboolean allow_mouse_drag;
|
||
|
+ GtkOrientation orientation;
|
||
|
+
|
||
|
+ gint start_x;
|
||
|
+ gint start_y;
|
||
|
+ gboolean use_capture_phase;
|
||
|
+
|
||
|
+ guint32 prev_time;
|
||
|
+ gdouble velocity;
|
||
|
+
|
||
|
+ gdouble initial_progress;
|
||
|
+ gdouble progress;
|
||
|
+ gboolean cancelled;
|
||
|
+
|
||
|
+ gdouble prev_offset;
|
||
|
+
|
||
|
+ gboolean is_scrolling;
|
||
|
+
|
||
|
+ GtkHdySwipeTrackerState state;
|
||
|
+ GtkGesture *touch_gesture;
|
||
|
+};
|
||
|
+
|
||
|
+G_DEFINE_TYPE_WITH_CODE (GtkHdySwipeTracker, gtk_hdy_swipe_tracker, G_TYPE_OBJECT,
|
||
|
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
|
||
|
+
|
||
|
+enum {
|
||
|
+ PROP_0,
|
||
|
+ PROP_SWIPEABLE,
|
||
|
+ PROP_ENABLED,
|
||
|
+ PROP_REVERSED,
|
||
|
+ PROP_ALLOW_MOUSE_DRAG,
|
||
|
+
|
||
|
+ /* GtkOrientable */
|
||
|
+ PROP_ORIENTATION,
|
||
|
+ LAST_PROP = PROP_ALLOW_MOUSE_DRAG + 1,
|
||
|
+};
|
||
|
+
|
||
|
+static GParamSpec *props[LAST_PROP];
|
||
|
+
|
||
|
+enum {
|
||
|
+ SIGNAL_BEGIN_SWIPE,
|
||
|
+ SIGNAL_UPDATE_SWIPE,
|
||
|
+ SIGNAL_END_SWIPE,
|
||
|
+ SIGNAL_LAST_SIGNAL,
|
||
|
+};
|
||
|
+
|
||
|
+static guint signals[SIGNAL_LAST_SIGNAL];
|
||
|
+
|
||
|
+static gboolean
|
||
|
+get_widget_coordinates (GtkHdySwipeTracker *self,
|
||
|
+ GdkEvent *event,
|
||
|
+ gdouble *x,
|
||
|
+ gdouble *y)
|
||
|
+{
|
||
|
+ GdkWindow *window = gdk_event_get_window (event);
|
||
|
+ gdouble tx, ty, out_x = -1, out_y = -1;
|
||
|
+
|
||
|
+ if (!gdk_event_get_coords (event, &tx, &ty))
|
||
|
+ goto out;
|
||
|
+
|
||
|
+ while (window && window != gtk_widget_get_window (GTK_WIDGET (self->swipeable))) {
|
||
|
+ gint window_x, window_y;
|
||
|
+
|
||
|
+ gdk_window_get_position (window, &window_x, &window_y);
|
||
|
+
|
||
|
+ tx += window_x;
|
||
|
+ ty += window_y;
|
||
|
+
|
||
|
+ window = gdk_window_get_parent (window);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (window) {
|
||
|
+ out_x = tx;
|
||
|
+ out_y = ty;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+out:
|
||
|
+ if (x)
|
||
|
+ *x = out_x;
|
||
|
+
|
||
|
+ if (y)
|
||
|
+ *y = out_y;
|
||
|
+
|
||
|
+ return out_x >= 0 && out_y >= 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+reset (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ self->state = GTK_HDY_SWIPE_TRACKER_STATE_NONE;
|
||
|
+
|
||
|
+ self->prev_offset = 0;
|
||
|
+
|
||
|
+ self->initial_progress = 0;
|
||
|
+ self->progress = 0;
|
||
|
+
|
||
|
+ self->start_x = 0;
|
||
|
+ self->start_y = 0;
|
||
|
+ self->use_capture_phase = FALSE;
|
||
|
+
|
||
|
+ self->prev_time = 0;
|
||
|
+ self->velocity = 0;
|
||
|
+
|
||
|
+ self->cancelled = FALSE;
|
||
|
+
|
||
|
+ if (self->swipeable)
|
||
|
+ gtk_grab_remove (GTK_WIDGET (self->swipeable));
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+get_range (GtkHdySwipeTracker *self,
|
||
|
+ gdouble *first,
|
||
|
+ gdouble *last)
|
||
|
+{
|
||
|
+ g_autofree gdouble *points = NULL;
|
||
|
+ gint n;
|
||
|
+
|
||
|
+ points = gtk_hdy_swipeable_get_snap_points (self->swipeable, &n);
|
||
|
+
|
||
|
+ *first = points[0];
|
||
|
+ *last = points[n - 1];
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gesture_prepare (GtkHdySwipeTracker *self,
|
||
|
+ GtkHdyNavigationDirection direction,
|
||
|
+ gboolean is_drag)
|
||
|
+{
|
||
|
+ GdkRectangle rect;
|
||
|
+
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_NONE)
|
||
|
+ return;
|
||
|
+
|
||
|
+ gtk_hdy_swipeable_get_swipe_area (self->swipeable, direction, is_drag, &rect);
|
||
|
+
|
||
|
+ if (self->start_x < rect.x ||
|
||
|
+ self->start_x >= rect.x + rect.width ||
|
||
|
+ self->start_y < rect.y ||
|
||
|
+ self->start_y >= rect.y + rect.height) {
|
||
|
+ self->state = GTK_HDY_SWIPE_TRACKER_STATE_REJECTED;
|
||
|
+
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ gtk_hdy_swipe_tracker_emit_begin_swipe (self, direction, TRUE);
|
||
|
+
|
||
|
+ self->initial_progress = gtk_hdy_swipeable_get_progress (self->swipeable);
|
||
|
+ self->progress = self->initial_progress;
|
||
|
+ self->velocity = 0;
|
||
|
+ self->state = GTK_HDY_SWIPE_TRACKER_STATE_PENDING;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gesture_begin (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ g_autoptr (GdkEvent) event = NULL;
|
||
|
+
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_PENDING)
|
||
|
+ return;
|
||
|
+
|
||
|
+ event = gtk_get_current_event ();
|
||
|
+ self->prev_time = gdk_event_get_time (event);
|
||
|
+ self->state = GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING;
|
||
|
+
|
||
|
+ gtk_grab_add (GTK_WIDGET (self->swipeable));
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gesture_update (GtkHdySwipeTracker *self,
|
||
|
+ gdouble delta)
|
||
|
+{
|
||
|
+ g_autoptr (GdkEvent) event = NULL;
|
||
|
+ guint32 time;
|
||
|
+ gdouble progress;
|
||
|
+ gdouble first_point, last_point;
|
||
|
+
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING)
|
||
|
+ return;
|
||
|
+
|
||
|
+ event = gtk_get_current_event ();
|
||
|
+ time = gdk_event_get_time (event);
|
||
|
+ if (time != self->prev_time)
|
||
|
+ self->velocity = delta / (time - self->prev_time);
|
||
|
+
|
||
|
+ get_range (self, &first_point, &last_point);
|
||
|
+
|
||
|
+ progress = self->progress + delta;
|
||
|
+ progress = CLAMP (progress, first_point, last_point);
|
||
|
+
|
||
|
+ /* FIXME: this is a hack to prevent swiping more than 1 page at once */
|
||
|
+ progress = CLAMP (progress, self->initial_progress - 1, self->initial_progress + 1);
|
||
|
+
|
||
|
+ self->progress = progress;
|
||
|
+
|
||
|
+ gtk_hdy_swipe_tracker_emit_update_swipe (self, progress);
|
||
|
+
|
||
|
+ self->prev_time = time;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+get_closest_snap_points (GtkHdySwipeTracker *self,
|
||
|
+ gdouble *upper,
|
||
|
+ gdouble *lower)
|
||
|
+{
|
||
|
+ gint i, n;
|
||
|
+ gdouble *points;
|
||
|
+
|
||
|
+ *upper = 0;
|
||
|
+ *lower = 0;
|
||
|
+
|
||
|
+ points = gtk_hdy_swipeable_get_snap_points (self->swipeable, &n);
|
||
|
+
|
||
|
+ for (i = 0; i < n; i++) {
|
||
|
+ if (points[i] >= self->progress) {
|
||
|
+ *upper = points[i];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ for (i = n - 1; i >= 0; i--) {
|
||
|
+ if (points[i] <= self->progress) {
|
||
|
+ *lower = points[i];
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ g_free (points);
|
||
|
+}
|
||
|
+
|
||
|
+static gdouble
|
||
|
+get_end_progress (GtkHdySwipeTracker *self,
|
||
|
+ gdouble distance)
|
||
|
+{
|
||
|
+ gdouble upper, lower, middle;
|
||
|
+
|
||
|
+ if (self->cancelled)
|
||
|
+ return gtk_hdy_swipeable_get_cancel_progress (self->swipeable);
|
||
|
+
|
||
|
+ get_closest_snap_points (self, &upper, &lower);
|
||
|
+ middle = (upper + lower) / 2;
|
||
|
+
|
||
|
+ if (self->progress > middle)
|
||
|
+ return (self->velocity * distance > -VELOCITY_THRESHOLD ||
|
||
|
+ self->initial_progress > upper) ? upper : lower;
|
||
|
+
|
||
|
+ return (self->velocity * distance < VELOCITY_THRESHOLD ||
|
||
|
+ self->initial_progress < lower) ? lower : upper;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gesture_end (GtkHdySwipeTracker *self,
|
||
|
+ gdouble distance)
|
||
|
+{
|
||
|
+ gdouble end_progress, velocity;
|
||
|
+ gint64 duration;
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_NONE)
|
||
|
+ return;
|
||
|
+
|
||
|
+ end_progress = get_end_progress (self, distance);
|
||
|
+
|
||
|
+ velocity = ANIMATION_BASE_VELOCITY;
|
||
|
+ if ((end_progress - self->progress) * self->velocity > 0)
|
||
|
+ velocity = self->velocity;
|
||
|
+
|
||
|
+ duration = ABS ((self->progress - end_progress) / velocity * DURATION_MULTIPLIER);
|
||
|
+ if (self->progress != end_progress)
|
||
|
+ duration = CLAMP (duration, MIN_ANIMATION_DURATION, MAX_ANIMATION_DURATION);
|
||
|
+
|
||
|
+ gtk_hdy_swipe_tracker_emit_end_swipe (self, duration, end_progress);
|
||
|
+
|
||
|
+ if (self->cancelled)
|
||
|
+ reset (self);
|
||
|
+ else
|
||
|
+ self->state = GTK_HDY_SWIPE_TRACKER_STATE_FINISHING;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gesture_cancel (GtkHdySwipeTracker *self,
|
||
|
+ gdouble distance)
|
||
|
+{
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_PENDING &&
|
||
|
+ self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ reset (self);
|
||
|
+
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ self->cancelled = TRUE;
|
||
|
+ gesture_end (self, distance);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+drag_begin_cb (GtkHdySwipeTracker *self,
|
||
|
+ gdouble start_x,
|
||
|
+ gdouble start_y,
|
||
|
+ GtkGestureDrag *gesture)
|
||
|
+{
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_NONE)
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+
|
||
|
+ self->start_x = start_x;
|
||
|
+ self->start_y = start_y;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+drag_update_cb (GtkHdySwipeTracker *self,
|
||
|
+ gdouble offset_x,
|
||
|
+ gdouble offset_y,
|
||
|
+ GtkGestureDrag *gesture)
|
||
|
+{
|
||
|
+ gdouble offset, distance;
|
||
|
+ gboolean is_vertical, is_offset_vertical;
|
||
|
+
|
||
|
+ distance = gtk_hdy_swipeable_get_distance (self->swipeable);
|
||
|
+
|
||
|
+ is_vertical = (self->orientation == GTK_ORIENTATION_VERTICAL);
|
||
|
+ if (is_vertical)
|
||
|
+ offset = -offset_y / distance;
|
||
|
+ else
|
||
|
+ offset = -offset_x / distance;
|
||
|
+
|
||
|
+ if (self->reversed)
|
||
|
+ offset = -offset;
|
||
|
+
|
||
|
+ is_offset_vertical = (ABS (offset_y) > ABS (offset_x));
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_REJECTED) {
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_NONE) {
|
||
|
+ if (is_vertical == is_offset_vertical)
|
||
|
+ gesture_prepare (self, offset > 0 ? GTK_HDY_NAVIGATION_DIRECTION_FORWARD : GTK_HDY_NAVIGATION_DIRECTION_BACK, TRUE);
|
||
|
+ else
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_PENDING) {
|
||
|
+ gdouble drag_distance;
|
||
|
+ gdouble first_point, last_point;
|
||
|
+ gboolean is_overshooting;
|
||
|
+
|
||
|
+ get_range (self, &first_point, &last_point);
|
||
|
+
|
||
|
+ drag_distance = sqrt (offset_x * offset_x + offset_y * offset_y);
|
||
|
+ is_overshooting = (offset < 0 && self->progress <= first_point) ||
|
||
|
+ (offset > 0 && self->progress >= last_point);
|
||
|
+
|
||
|
+ if (drag_distance >= DRAG_THRESHOLD_DISTANCE) {
|
||
|
+ if ((is_vertical == is_offset_vertical) && !is_overshooting) {
|
||
|
+ gesture_begin (self);
|
||
|
+ self->prev_offset = offset;
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_CLAIMED);
|
||
|
+ } else {
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ gesture_update (self, offset - self->prev_offset);
|
||
|
+ self->prev_offset = offset;
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+drag_end_cb (GtkHdySwipeTracker *self,
|
||
|
+ gdouble offset_x,
|
||
|
+ gdouble offset_y,
|
||
|
+ GtkGestureDrag *gesture)
|
||
|
+{
|
||
|
+ gdouble distance;
|
||
|
+
|
||
|
+ distance = gtk_hdy_swipeable_get_distance (self->swipeable);
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_REJECTED) {
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+
|
||
|
+ reset (self);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ gesture_cancel (self, distance);
|
||
|
+ gtk_gesture_set_state (self->touch_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ gesture_end (self, distance);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+drag_cancel_cb (GtkHdySwipeTracker *self,
|
||
|
+ GdkEventSequence *sequence,
|
||
|
+ GtkGesture *gesture)
|
||
|
+{
|
||
|
+ gdouble distance;
|
||
|
+
|
||
|
+ distance = gtk_hdy_swipeable_get_distance (self->swipeable);
|
||
|
+
|
||
|
+ gesture_cancel (self, distance);
|
||
|
+ gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||
|
+}
|
||
|
+
|
||
|
+static gboolean
|
||
|
+handle_scroll_event (GtkHdySwipeTracker *self,
|
||
|
+ GdkEvent *event,
|
||
|
+ gboolean capture)
|
||
|
+{
|
||
|
+ GdkDevice *source_device;
|
||
|
+ GdkInputSource input_source;
|
||
|
+ gdouble dx, dy, delta, distance;
|
||
|
+ gboolean is_vertical;
|
||
|
+ gboolean is_delta_vertical;
|
||
|
+
|
||
|
+ is_vertical = (self->orientation == GTK_ORIENTATION_VERTICAL);
|
||
|
+ distance = is_vertical ? TOUCHPAD_BASE_DISTANCE_V : TOUCHPAD_BASE_DISTANCE_H;
|
||
|
+
|
||
|
+ if (gdk_event_get_scroll_direction (event, NULL))
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ source_device = gdk_event_get_source_device (event);
|
||
|
+ input_source = gdk_device_get_source (source_device);
|
||
|
+ if (input_source != GDK_SOURCE_TOUCHPAD)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ gdk_event_get_scroll_deltas (event, &dx, &dy);
|
||
|
+ delta = is_vertical ? dy : dx;
|
||
|
+ if (self->reversed)
|
||
|
+ delta = -delta;
|
||
|
+
|
||
|
+ is_delta_vertical = (ABS (dy) > ABS (dx));
|
||
|
+
|
||
|
+ if (self->is_scrolling) {
|
||
|
+ gesture_cancel (self, distance);
|
||
|
+
|
||
|
+ if (gdk_event_is_scroll_stop_event (event))
|
||
|
+ self->is_scrolling = FALSE;
|
||
|
+
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_REJECTED) {
|
||
|
+ if (gdk_event_is_scroll_stop_event (event))
|
||
|
+ reset (self);
|
||
|
+
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_NONE) {
|
||
|
+ if (gdk_event_is_scroll_stop_event (event))
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ if (is_vertical == is_delta_vertical) {
|
||
|
+ if (!capture) {
|
||
|
+ gdouble event_x, event_y;
|
||
|
+
|
||
|
+ get_widget_coordinates (self, event, &event_x, &event_y);
|
||
|
+
|
||
|
+ self->start_x = (gint) round (event_x);
|
||
|
+ self->start_y = (gint) round (event_y);
|
||
|
+
|
||
|
+ gesture_prepare (self, delta > 0 ? GTK_HDY_NAVIGATION_DIRECTION_FORWARD : GTK_HDY_NAVIGATION_DIRECTION_BACK, FALSE);
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ self->is_scrolling = TRUE;
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!capture && self->state == GTK_HDY_SWIPE_TRACKER_STATE_PENDING) {
|
||
|
+ gboolean is_overshooting;
|
||
|
+ gdouble first_point, last_point;
|
||
|
+
|
||
|
+ get_range (self, &first_point, &last_point);
|
||
|
+
|
||
|
+ is_overshooting = (delta < 0 && self->progress <= first_point) ||
|
||
|
+ (delta > 0 && self->progress >= last_point);
|
||
|
+
|
||
|
+ if ((is_vertical == is_delta_vertical) && !is_overshooting)
|
||
|
+ gesture_begin (self);
|
||
|
+ else
|
||
|
+ gesture_cancel (self, distance);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ if (gdk_event_is_scroll_stop_event (event)) {
|
||
|
+ gesture_end (self, distance);
|
||
|
+ } else {
|
||
|
+ gesture_update (self, delta / distance * SCROLL_MULTIPLIER);
|
||
|
+ return GDK_EVENT_STOP;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!capture && self->state == GTK_HDY_SWIPE_TRACKER_STATE_FINISHING)
|
||
|
+ reset (self);
|
||
|
+
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+}
|
||
|
+
|
||
|
+static gboolean
|
||
|
+is_window_handle (GtkWidget *widget)
|
||
|
+{
|
||
|
+ gboolean window_dragging;
|
||
|
+ GtkWidget *parent, *window, *titlebar;
|
||
|
+
|
||
|
+ gtk_widget_style_get (widget, "window-dragging", &window_dragging, NULL);
|
||
|
+
|
||
|
+ if (window_dragging)
|
||
|
+ return TRUE;
|
||
|
+
|
||
|
+ /* Window titlebar area is always draggable, so check if we're inside. */
|
||
|
+ window = gtk_widget_get_toplevel (widget);
|
||
|
+ if (!GTK_IS_WINDOW (window))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ titlebar = gtk_window_get_titlebar (GTK_WINDOW (window));
|
||
|
+ if (!titlebar)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ parent = widget;
|
||
|
+ while (parent && parent != titlebar)
|
||
|
+ parent = gtk_widget_get_parent (parent);
|
||
|
+
|
||
|
+ return parent == titlebar;
|
||
|
+}
|
||
|
+
|
||
|
+static gboolean
|
||
|
+has_conflicts (GtkHdySwipeTracker *self,
|
||
|
+ GtkWidget *widget)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *other;
|
||
|
+
|
||
|
+ if (widget == GTK_WIDGET (self->swipeable))
|
||
|
+ return TRUE;
|
||
|
+
|
||
|
+ if (!GTK_IS_HDY_SWIPEABLE (widget))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ other = gtk_hdy_swipeable_get_swipe_tracker (GTK_HDY_SWIPEABLE (widget));
|
||
|
+
|
||
|
+ return self->orientation == other->orientation;
|
||
|
+}
|
||
|
+
|
||
|
+/* HACK: Since we don't have _gtk_widget_consumes_motion(), we can't do a proper
|
||
|
+ * check for whether we can drag from a widget or not. So we trust the widgets
|
||
|
+ * to propagate or stop their events. However, GtkButton stops press events,
|
||
|
+ * making it impossible to drag from it.
|
||
|
+ */
|
||
|
+static gboolean
|
||
|
+should_force_drag (GtkHdySwipeTracker *self,
|
||
|
+ GtkWidget *widget)
|
||
|
+{
|
||
|
+ GtkWidget *parent;
|
||
|
+
|
||
|
+ if (!GTK_IS_BUTTON (widget))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ parent = widget;
|
||
|
+ while (parent && !has_conflicts (self, parent))
|
||
|
+ parent = gtk_widget_get_parent (parent);
|
||
|
+
|
||
|
+ return parent == GTK_WIDGET (self->swipeable);
|
||
|
+}
|
||
|
+
|
||
|
+static gboolean
|
||
|
+handle_event_cb (GtkHdySwipeTracker *self,
|
||
|
+ GdkEvent *event)
|
||
|
+{
|
||
|
+ GdkEventSequence *sequence;
|
||
|
+ gboolean retval;
|
||
|
+ GtkEventSequenceState state;
|
||
|
+ GtkWidget *widget;
|
||
|
+
|
||
|
+ if (!self->enabled && self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ if (self->use_capture_phase)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ if (event->type == GDK_SCROLL)
|
||
|
+ return handle_scroll_event (self, event, FALSE);
|
||
|
+
|
||
|
+ if (event->type != GDK_BUTTON_PRESS &&
|
||
|
+ event->type != GDK_BUTTON_RELEASE &&
|
||
|
+ event->type != GDK_MOTION_NOTIFY &&
|
||
|
+ event->type != GDK_TOUCH_BEGIN &&
|
||
|
+ event->type != GDK_TOUCH_END &&
|
||
|
+ event->type != GDK_TOUCH_UPDATE &&
|
||
|
+ event->type != GDK_TOUCH_CANCEL)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ widget = gtk_get_event_widget (event);
|
||
|
+ if (is_window_handle (widget))
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ sequence = gdk_event_get_event_sequence (event);
|
||
|
+ retval = gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (self->touch_gesture), event);
|
||
|
+ state = gtk_gesture_get_sequence_state (self->touch_gesture, sequence);
|
||
|
+
|
||
|
+ if (state == GTK_EVENT_SEQUENCE_DENIED) {
|
||
|
+ gtk_event_controller_reset (GTK_EVENT_CONTROLLER (self->touch_gesture));
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ return GDK_EVENT_STOP;
|
||
|
+ } else if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_FINISHING) {
|
||
|
+ reset (self);
|
||
|
+ return GDK_EVENT_STOP;
|
||
|
+ }
|
||
|
+ return retval;
|
||
|
+}
|
||
|
+
|
||
|
+static gboolean
|
||
|
+captured_event_cb (GtkHdySwipeable *swipeable,
|
||
|
+ GdkEvent *event)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *self = gtk_hdy_swipeable_get_swipe_tracker (swipeable);
|
||
|
+ GtkWidget *widget;
|
||
|
+ GdkEventSequence *sequence;
|
||
|
+ gboolean retval;
|
||
|
+ GtkEventSequenceState state;
|
||
|
+
|
||
|
+ g_assert (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ if (!self->enabled && self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ if (event->type == GDK_SCROLL)
|
||
|
+ return handle_scroll_event (self, event, TRUE);
|
||
|
+
|
||
|
+ if (event->type != GDK_BUTTON_PRESS &&
|
||
|
+ event->type != GDK_BUTTON_RELEASE &&
|
||
|
+ event->type != GDK_MOTION_NOTIFY &&
|
||
|
+ event->type != GDK_TOUCH_BEGIN &&
|
||
|
+ event->type != GDK_TOUCH_END &&
|
||
|
+ event->type != GDK_TOUCH_UPDATE &&
|
||
|
+ event->type != GDK_TOUCH_CANCEL)
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ widget = gtk_get_event_widget (event);
|
||
|
+
|
||
|
+ if (!self->use_capture_phase && !should_force_drag (self, widget))
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+
|
||
|
+ sequence = gdk_event_get_event_sequence (event);
|
||
|
+
|
||
|
+ if (gtk_gesture_handles_sequence (self->touch_gesture, sequence))
|
||
|
+ self->use_capture_phase = TRUE;
|
||
|
+
|
||
|
+ retval = gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (self->touch_gesture), event);
|
||
|
+ state = gtk_gesture_get_sequence_state (self->touch_gesture, sequence);
|
||
|
+
|
||
|
+ if (state == GTK_EVENT_SEQUENCE_DENIED) {
|
||
|
+ gtk_event_controller_reset (GTK_EVENT_CONTROLLER (self->touch_gesture));
|
||
|
+ return GDK_EVENT_PROPAGATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING) {
|
||
|
+ return GDK_EVENT_STOP;
|
||
|
+ } else if (self->state == GTK_HDY_SWIPE_TRACKER_STATE_FINISHING) {
|
||
|
+ reset (self);
|
||
|
+ return GDK_EVENT_STOP;
|
||
|
+ }
|
||
|
+
|
||
|
+ return retval;
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_constructed (GObject *object)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *self = GTK_HDY_SWIPE_TRACKER (object);
|
||
|
+
|
||
|
+ g_assert (self->swipeable);
|
||
|
+
|
||
|
+ gtk_widget_add_events (GTK_WIDGET (self->swipeable),
|
||
|
+ GDK_SMOOTH_SCROLL_MASK |
|
||
|
+ GDK_BUTTON_PRESS_MASK |
|
||
|
+ GDK_BUTTON_RELEASE_MASK |
|
||
|
+ GDK_BUTTON_MOTION_MASK |
|
||
|
+ GDK_TOUCH_MASK);
|
||
|
+
|
||
|
+ self->touch_gesture = g_object_new (GTK_TYPE_GESTURE_DRAG,
|
||
|
+ "widget", self->swipeable,
|
||
|
+ "propagation-phase", GTK_PHASE_NONE,
|
||
|
+ "touch-only", !self->allow_mouse_drag,
|
||
|
+ NULL);
|
||
|
+
|
||
|
+ g_signal_connect_swapped (self->touch_gesture, "drag-begin", G_CALLBACK (drag_begin_cb), self);
|
||
|
+ g_signal_connect_swapped (self->touch_gesture, "drag-update", G_CALLBACK (drag_update_cb), self);
|
||
|
+ g_signal_connect_swapped (self->touch_gesture, "drag-end", G_CALLBACK (drag_end_cb), self);
|
||
|
+ g_signal_connect_swapped (self->touch_gesture, "cancel", G_CALLBACK (drag_cancel_cb), self);
|
||
|
+
|
||
|
+ g_signal_connect_object (self->swipeable, "event", G_CALLBACK (handle_event_cb), self, G_CONNECT_SWAPPED);
|
||
|
+ g_signal_connect_object (self->swipeable, "unrealize", G_CALLBACK (reset), self, G_CONNECT_SWAPPED);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * HACK: GTK3 has no other way to get events on capture phase.
|
||
|
+ * This is a reimplementation of _gtk_widget_set_captured_event_handler(),
|
||
|
+ * which is private. In GTK4 it can be replaced with GtkEventControllerLegacy
|
||
|
+ * with capture propagation phase
|
||
|
+ */
|
||
|
+ g_object_set_data (G_OBJECT (self->swipeable), "captured-event-handler", captured_event_cb);
|
||
|
+
|
||
|
+ G_OBJECT_CLASS (gtk_hdy_swipe_tracker_parent_class)->constructed (object);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_dispose (GObject *object)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *self = GTK_HDY_SWIPE_TRACKER (object);
|
||
|
+
|
||
|
+ if (self->swipeable)
|
||
|
+ gtk_grab_remove (GTK_WIDGET (self->swipeable));
|
||
|
+
|
||
|
+ if (self->touch_gesture)
|
||
|
+ g_signal_handlers_disconnect_by_data (self->touch_gesture, self);
|
||
|
+
|
||
|
+ g_object_set_data (G_OBJECT (self->swipeable), "captured-event-handler", NULL);
|
||
|
+
|
||
|
+ g_clear_object (&self->touch_gesture);
|
||
|
+ g_clear_object (&self->swipeable);
|
||
|
+
|
||
|
+ G_OBJECT_CLASS (gtk_hdy_swipe_tracker_parent_class)->dispose (object);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_get_property (GObject *object,
|
||
|
+ guint prop_id,
|
||
|
+ GValue *value,
|
||
|
+ GParamSpec *pspec)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *self = GTK_HDY_SWIPE_TRACKER (object);
|
||
|
+
|
||
|
+ switch (prop_id) {
|
||
|
+ case PROP_SWIPEABLE:
|
||
|
+ g_value_set_object (value, gtk_hdy_swipe_tracker_get_swipeable (self));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ENABLED:
|
||
|
+ g_value_set_boolean (value, gtk_hdy_swipe_tracker_get_enabled (self));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_REVERSED:
|
||
|
+ g_value_set_boolean (value, gtk_hdy_swipe_tracker_get_reversed (self));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ALLOW_MOUSE_DRAG:
|
||
|
+ g_value_set_boolean (value, gtk_hdy_swipe_tracker_get_allow_mouse_drag (self));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ORIENTATION:
|
||
|
+ g_value_set_enum (value, self->orientation);
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_set_property (GObject *object,
|
||
|
+ guint prop_id,
|
||
|
+ const GValue *value,
|
||
|
+ GParamSpec *pspec)
|
||
|
+{
|
||
|
+ GtkHdySwipeTracker *self = GTK_HDY_SWIPE_TRACKER (object);
|
||
|
+
|
||
|
+ switch (prop_id) {
|
||
|
+ case PROP_SWIPEABLE:
|
||
|
+ self->swipeable = GTK_HDY_SWIPEABLE (g_object_ref (g_value_get_object (value)));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ENABLED:
|
||
|
+ gtk_hdy_swipe_tracker_set_enabled (self, g_value_get_boolean (value));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_REVERSED:
|
||
|
+ gtk_hdy_swipe_tracker_set_reversed (self, g_value_get_boolean (value));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ALLOW_MOUSE_DRAG:
|
||
|
+ gtk_hdy_swipe_tracker_set_allow_mouse_drag (self, g_value_get_boolean (value));
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROP_ORIENTATION:
|
||
|
+ {
|
||
|
+ GtkOrientation orientation = g_value_get_enum (value);
|
||
|
+ if (orientation != self->orientation) {
|
||
|
+ self->orientation = g_value_get_enum (value);
|
||
|
+ g_object_notify (G_OBJECT (self), "orientation");
|
||
|
+ }
|
||
|
+ }
|
||
|
+ break;
|
||
|
+
|
||
|
+ default:
|
||
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_class_init (GtkHdySwipeTrackerClass *klass)
|
||
|
+{
|
||
|
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
+
|
||
|
+ object_class->constructed = gtk_hdy_swipe_tracker_constructed;
|
||
|
+ object_class->dispose = gtk_hdy_swipe_tracker_dispose;
|
||
|
+ object_class->get_property = gtk_hdy_swipe_tracker_get_property;
|
||
|
+ object_class->set_property = gtk_hdy_swipe_tracker_set_property;
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker:swipeable:
|
||
|
+ *
|
||
|
+ * The widget the swipe tracker is attached to. Must not be %NULL.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ props[PROP_SWIPEABLE] =
|
||
|
+ g_param_spec_object ("swipeable",
|
||
|
+ _("Swipeable"),
|
||
|
+ _("The swipeable the swipe tracker is attached to"),
|
||
|
+ GTK_TYPE_HDY_SWIPEABLE,
|
||
|
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker:enabled:
|
||
|
+ *
|
||
|
+ * Whether the swipe tracker is enabled. When it's not enabled, no events
|
||
|
+ * will be processed. Usually widgets will want to expose this via a property.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ props[PROP_ENABLED] =
|
||
|
+ g_param_spec_boolean ("enabled",
|
||
|
+ _("Enabled"),
|
||
|
+ _("Whether the swipe tracker processes events"),
|
||
|
+ TRUE,
|
||
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker:reversed:
|
||
|
+ *
|
||
|
+ * Whether to reverse the swipe direction. If the swipe tracker is horizontal,
|
||
|
+ * it can be used for supporting RTL text direction.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ props[PROP_REVERSED] =
|
||
|
+ g_param_spec_boolean ("reversed",
|
||
|
+ _("Reversed"),
|
||
|
+ _("Whether swipe direction is reversed"),
|
||
|
+ FALSE,
|
||
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker:allow-mouse-drag:
|
||
|
+ *
|
||
|
+ * Whether to allow dragging with mouse pointer. This should usually be
|
||
|
+ * %FALSE.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ props[PROP_ALLOW_MOUSE_DRAG] =
|
||
|
+ g_param_spec_boolean ("allow-mouse-drag",
|
||
|
+ _("Allow mouse drag"),
|
||
|
+ _("Whether to allow dragging with mouse pointer"),
|
||
|
+ FALSE,
|
||
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||
|
+
|
||
|
+ g_object_class_override_property (object_class,
|
||
|
+ PROP_ORIENTATION,
|
||
|
+ "orientation");
|
||
|
+
|
||
|
+ g_object_class_install_properties (object_class, LAST_PROP, props);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker::begin-swipe:
|
||
|
+ * @self: The #GtkHdySwipeTracker instance
|
||
|
+ * @direction: The direction of the swipe
|
||
|
+ * @direct: %TRUE if the swipe is directly triggered by a gesture,
|
||
|
+ * %FALSE if it's triggered via a #GtkHdySwipeGroup
|
||
|
+ *
|
||
|
+ * This signal is emitted when a possible swipe is detected.
|
||
|
+ *
|
||
|
+ * The @direction value can be used to restrict the swipe to a certain
|
||
|
+ * direction.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ signals[SIGNAL_BEGIN_SWIPE] =
|
||
|
+ g_signal_new ("begin-swipe",
|
||
|
+ G_TYPE_FROM_CLASS (klass),
|
||
|
+ G_SIGNAL_RUN_FIRST,
|
||
|
+ 0,
|
||
|
+ NULL, NULL, NULL,
|
||
|
+ G_TYPE_NONE,
|
||
|
+ 2,
|
||
|
+ GTK_TYPE_HDY_NAVIGATION_DIRECTION, G_TYPE_BOOLEAN);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker::update-swipe:
|
||
|
+ * @self: The #GtkHdySwipeTracker instance
|
||
|
+ * @progress: The current animation progress value
|
||
|
+ *
|
||
|
+ * This signal is emitted every time the progress value changes.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ signals[SIGNAL_UPDATE_SWIPE] =
|
||
|
+ g_signal_new ("update-swipe",
|
||
|
+ G_TYPE_FROM_CLASS (klass),
|
||
|
+ G_SIGNAL_RUN_FIRST,
|
||
|
+ 0,
|
||
|
+ NULL, NULL, NULL,
|
||
|
+ G_TYPE_NONE,
|
||
|
+ 1,
|
||
|
+ G_TYPE_DOUBLE);
|
||
|
+
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeTracker::end-swipe:
|
||
|
+ * @self: The #GtkHdySwipeTracker instance
|
||
|
+ * @duration: Snap-back animation duration in milliseconds
|
||
|
+ * @to: The progress value to animate to
|
||
|
+ *
|
||
|
+ * This signal is emitted as soon as the gesture has stopped.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ signals[SIGNAL_END_SWIPE] =
|
||
|
+ g_signal_new ("end-swipe",
|
||
|
+ G_TYPE_FROM_CLASS (klass),
|
||
|
+ G_SIGNAL_RUN_FIRST,
|
||
|
+ 0,
|
||
|
+ NULL, NULL, NULL,
|
||
|
+ G_TYPE_NONE,
|
||
|
+ 2,
|
||
|
+ G_TYPE_INT64, G_TYPE_DOUBLE);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipe_tracker_init (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ reset (self);
|
||
|
+ self->orientation = GTK_ORIENTATION_HORIZONTAL;
|
||
|
+ self->enabled = TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_new:
|
||
|
+ * @swipeable: a #GtkWidget to add the tracker on
|
||
|
+ *
|
||
|
+ * Create a new #GtkHdySwipeTracker object on @widget.
|
||
|
+ *
|
||
|
+ * Returns: the newly created #GtkHdySwipeTracker object
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+GtkHdySwipeTracker *
|
||
|
+gtk_hdy_swipe_tracker_new (GtkHdySwipeable *swipeable)
|
||
|
+{
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (swipeable), NULL);
|
||
|
+
|
||
|
+ return g_object_new (GTK_TYPE_HDY_SWIPE_TRACKER,
|
||
|
+ "swipeable", swipeable,
|
||
|
+ NULL);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_get_swipeable:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ *
|
||
|
+ * Get @self's swipeable widget.
|
||
|
+ *
|
||
|
+ * Returns: (transfer none): the swipeable widget
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+GtkHdySwipeable *
|
||
|
+gtk_hdy_swipe_tracker_get_swipeable (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self), NULL);
|
||
|
+
|
||
|
+ return self->swipeable;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_get_enabled:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ *
|
||
|
+ * Get whether @self is enabled. When it's not enabled, no events will be
|
||
|
+ * processed. Generally widgets will want to expose this via a property.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE if @self is enabled
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gboolean
|
||
|
+gtk_hdy_swipe_tracker_get_enabled (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self), FALSE);
|
||
|
+
|
||
|
+ return self->enabled;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_set_enabled:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ * @enabled: whether to enable to swipe tracker
|
||
|
+ *
|
||
|
+ * Set whether @self is enabled. When it's not enabled, no events will be
|
||
|
+ * processed. Usually widgets will want to expose this via a property.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_set_enabled (GtkHdySwipeTracker *self,
|
||
|
+ gboolean enabled)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ enabled = !!enabled;
|
||
|
+
|
||
|
+ if (self->enabled == enabled)
|
||
|
+ return;
|
||
|
+
|
||
|
+ self->enabled = enabled;
|
||
|
+
|
||
|
+ if (!enabled && self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING)
|
||
|
+ reset (self);
|
||
|
+
|
||
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ENABLED]);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_get_reversed:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ *
|
||
|
+ * Get whether @self is reversing the swipe direction.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE is the direction is reversed
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gboolean
|
||
|
+gtk_hdy_swipe_tracker_get_reversed (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self), FALSE);
|
||
|
+
|
||
|
+ return self->reversed;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_set_reversed:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ * @reversed: whether to reverse the swipe direction
|
||
|
+ *
|
||
|
+ * Set whether to reverse the swipe direction. If @self is horizontal,
|
||
|
+ * can be used for supporting RTL text direction.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_set_reversed (GtkHdySwipeTracker *self,
|
||
|
+ gboolean reversed)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ reversed = !!reversed;
|
||
|
+
|
||
|
+ if (self->reversed == reversed)
|
||
|
+ return;
|
||
|
+
|
||
|
+ self->reversed = reversed;
|
||
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REVERSED]);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_get_allow_mouse_drag:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ *
|
||
|
+ * Get whether @self can be dragged with mouse pointer.
|
||
|
+ *
|
||
|
+ * Returns: %TRUE is mouse dragging is allowed
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gboolean
|
||
|
+gtk_hdy_swipe_tracker_get_allow_mouse_drag (GtkHdySwipeTracker *self)
|
||
|
+{
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self), FALSE);
|
||
|
+
|
||
|
+ return self->allow_mouse_drag;
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_set_allow_mouse_drag:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ * @allow_mouse_drag: whether to allow mouse dragging
|
||
|
+ *
|
||
|
+ * Set whether @self can be dragged with mouse pointer. This should usually be
|
||
|
+ * %FALSE.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_set_allow_mouse_drag (GtkHdySwipeTracker *self,
|
||
|
+ gboolean allow_mouse_drag)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ allow_mouse_drag = !!allow_mouse_drag;
|
||
|
+
|
||
|
+ if (self->allow_mouse_drag == allow_mouse_drag)
|
||
|
+ return;
|
||
|
+
|
||
|
+ self->allow_mouse_drag = allow_mouse_drag;
|
||
|
+
|
||
|
+ if (self->touch_gesture)
|
||
|
+ g_object_set (self->touch_gesture, "touch-only", !allow_mouse_drag, NULL);
|
||
|
+
|
||
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ALLOW_MOUSE_DRAG]);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipe_tracker_shift_position:
|
||
|
+ * @self: a #GtkHdySwipeTracker
|
||
|
+ * @delta: the position delta
|
||
|
+ *
|
||
|
+ * Move the current progress value by @delta. This can be used to adjust the
|
||
|
+ * current position if snap points move during the gesture.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_shift_position (GtkHdySwipeTracker *self,
|
||
|
+ gdouble delta)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ if (self->state != GTK_HDY_SWIPE_TRACKER_STATE_PENDING &&
|
||
|
+ self->state != GTK_HDY_SWIPE_TRACKER_STATE_SCROLLING)
|
||
|
+ return;
|
||
|
+
|
||
|
+ self->progress += delta;
|
||
|
+ self->initial_progress += delta;
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_emit_begin_swipe (GtkHdySwipeTracker *self,
|
||
|
+ GtkHdyNavigationDirection direction,
|
||
|
+ gboolean direct)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ g_signal_emit (self, signals[SIGNAL_BEGIN_SWIPE], 0, direction, direct);
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_emit_update_swipe (GtkHdySwipeTracker *self,
|
||
|
+ gdouble progress)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ g_signal_emit (self, signals[SIGNAL_UPDATE_SWIPE], 0, progress);
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+gtk_hdy_swipe_tracker_emit_end_swipe (GtkHdySwipeTracker *self,
|
||
|
+ gint64 duration,
|
||
|
+ gdouble to)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPE_TRACKER (self));
|
||
|
+
|
||
|
+ g_signal_emit (self, signals[SIGNAL_END_SWIPE], 0, duration, to);
|
||
|
+}
|
||
|
diff --git a/gtk/hdy-swipeable-private.h b/gtk/hdy-swipeable-private.h
|
||
|
new file mode 100644
|
||
|
index 0000000..3c7ac4e
|
||
|
--- /dev/null
|
||
|
+++ b/gtk/hdy-swipeable-private.h
|
||
|
@@ -0,0 +1,82 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
|
||
|
+ *
|
||
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
||
|
+ */
|
||
|
+
|
||
|
+#pragma once
|
||
|
+
|
||
|
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||
|
+#error "Only <gtk/gtk.h> can be included directly."
|
||
|
+#endif
|
||
|
+
|
||
|
+#include <gdk/gdk.h>
|
||
|
+#include "gtkwidget.h"
|
||
|
+#include "hdy-navigation-direction-private.h"
|
||
|
+
|
||
|
+G_BEGIN_DECLS
|
||
|
+
|
||
|
+typedef struct _GtkHdySwipeTracker GtkHdySwipeTracker;
|
||
|
+
|
||
|
+#define GTK_TYPE_HDY_SWIPEABLE (gtk_hdy_swipeable_get_type ())
|
||
|
+
|
||
|
+G_DECLARE_INTERFACE (GtkHdySwipeable, gtk_hdy_swipeable, GTK, HDY_SWIPEABLE, GtkWidget)
|
||
|
+
|
||
|
+/**
|
||
|
+ * GtkHdySwipeableInterface:
|
||
|
+ * @parent: The parent interface.
|
||
|
+ * @switch_child: Switches visible child.
|
||
|
+ * @get_swipe_tracker: Gets the swipe tracker.
|
||
|
+ * @get_distance: Gets the swipe distance.
|
||
|
+ * @get_snap_points: Gets the snap points
|
||
|
+ * @get_progress: Gets the current progress.
|
||
|
+ * @get_cancel_progress: Gets the cancel progress.
|
||
|
+ * @get_swipe_area: Gets the swipeable rectangle.
|
||
|
+ *
|
||
|
+ * An interface for swipeable widgets.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ **/
|
||
|
+struct _GtkHdySwipeableInterface
|
||
|
+{
|
||
|
+ GTypeInterface parent;
|
||
|
+
|
||
|
+ void (*switch_child) (GtkHdySwipeable *self,
|
||
|
+ guint index,
|
||
|
+ gint64 duration);
|
||
|
+
|
||
|
+ GtkHdySwipeTracker * (*get_swipe_tracker) (GtkHdySwipeable *self);
|
||
|
+ gdouble (*get_distance) (GtkHdySwipeable *self);
|
||
|
+ gdouble * (*get_snap_points) (GtkHdySwipeable *self,
|
||
|
+ gint *n_snap_points);
|
||
|
+ gdouble (*get_progress) (GtkHdySwipeable *self);
|
||
|
+ gdouble (*get_cancel_progress) (GtkHdySwipeable *self);
|
||
|
+ void (*get_swipe_area) (GtkHdySwipeable *self,
|
||
|
+ GtkHdyNavigationDirection navigation_direction,
|
||
|
+ gboolean is_drag,
|
||
|
+ GdkRectangle *rect);
|
||
|
+
|
||
|
+ /*< private >*/
|
||
|
+ gpointer padding[4];
|
||
|
+};
|
||
|
+
|
||
|
+void gtk_hdy_swipeable_switch_child (GtkHdySwipeable *self,
|
||
|
+ guint index,
|
||
|
+ gint64 duration);
|
||
|
+
|
||
|
+void gtk_hdy_swipeable_emit_child_switched (GtkHdySwipeable *self,
|
||
|
+ guint index,
|
||
|
+ gint64 duration);
|
||
|
+
|
||
|
+GtkHdySwipeTracker *gtk_hdy_swipeable_get_swipe_tracker (GtkHdySwipeable *self);
|
||
|
+gdouble gtk_hdy_swipeable_get_distance (GtkHdySwipeable *self);
|
||
|
+gdouble *gtk_hdy_swipeable_get_snap_points (GtkHdySwipeable *self,
|
||
|
+ gint *n_snap_points);
|
||
|
+gdouble gtk_hdy_swipeable_get_progress (GtkHdySwipeable *self);
|
||
|
+gdouble gtk_hdy_swipeable_get_cancel_progress (GtkHdySwipeable *self);
|
||
|
+void gtk_hdy_swipeable_get_swipe_area (GtkHdySwipeable *self,
|
||
|
+ GtkHdyNavigationDirection navigation_direction,
|
||
|
+ gboolean is_drag,
|
||
|
+ GdkRectangle *rect);
|
||
|
+
|
||
|
+G_END_DECLS
|
||
|
diff --git a/gtk/hdy-swipeable.c b/gtk/hdy-swipeable.c
|
||
|
new file mode 100644
|
||
|
index 0000000..916abd9
|
||
|
--- /dev/null
|
||
|
+++ b/gtk/hdy-swipeable.c
|
||
|
@@ -0,0 +1,274 @@
|
||
|
+/*
|
||
|
+ * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
|
||
|
+ *
|
||
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
||
|
+ */
|
||
|
+
|
||
|
+#include "config.h"
|
||
|
+
|
||
|
+#include "hdy-swipeable-private.h"
|
||
|
+#include "hdy-swipe-tracker-private.h"
|
||
|
+
|
||
|
+/**
|
||
|
+ * SECTION:gtk_hdy-swipeable
|
||
|
+ * @short_description: An interface for swipeable widgets.
|
||
|
+ * @title: GtkHdySwipeable
|
||
|
+ * @See_also: #GtkHdyCarousel, #GtkHdyDeck, #GtkHdyLeaflet, #GtkHdySwipeGroup
|
||
|
+ *
|
||
|
+ * The #GtkHdySwipeable interface is implemented by all swipeable widgets. They
|
||
|
+ * can be synced using #GtkHdySwipeGroup.
|
||
|
+ *
|
||
|
+ * See #GtkHdySwipeTracker for details about implementing it.
|
||
|
+ *
|
||
|
+ * Since: 0.0.12
|
||
|
+ */
|
||
|
+
|
||
|
+G_DEFINE_INTERFACE (GtkHdySwipeable, gtk_hdy_swipeable, GTK_TYPE_WIDGET)
|
||
|
+
|
||
|
+enum {
|
||
|
+ SIGNAL_CHILD_SWITCHED,
|
||
|
+ SIGNAL_LAST_SIGNAL,
|
||
|
+};
|
||
|
+
|
||
|
+static guint signals[SIGNAL_LAST_SIGNAL];
|
||
|
+
|
||
|
+static void
|
||
|
+gtk_hdy_swipeable_default_init (GtkHdySwipeableInterface *iface)
|
||
|
+{
|
||
|
+ /**
|
||
|
+ * GtkHdySwipeable::child-switched:
|
||
|
+ * @self: The #GtkHdySwipeable instance
|
||
|
+ * @index: the index of the child to switch to
|
||
|
+ * @duration: Animation duration in milliseconds
|
||
|
+ *
|
||
|
+ * This signal should be emitted when the widget's visible child is changed.
|
||
|
+ *
|
||
|
+ * @duration can be 0 if the child is switched without animation.
|
||
|
+ *
|
||
|
+ * This is used by #GtkHdySwipeGroup, applications should not connect to it.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+ signals[SIGNAL_CHILD_SWITCHED] =
|
||
|
+ g_signal_new ("child-switched",
|
||
|
+ G_TYPE_FROM_INTERFACE (iface),
|
||
|
+ G_SIGNAL_RUN_FIRST,
|
||
|
+ 0,
|
||
|
+ NULL, NULL, NULL,
|
||
|
+ G_TYPE_NONE,
|
||
|
+ 2,
|
||
|
+ G_TYPE_UINT, G_TYPE_INT64);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_switch_child:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ * @index: the index of the child to switch to
|
||
|
+ * @duration: Animation duration in milliseconds
|
||
|
+ *
|
||
|
+ * See GtkHdySwipeable::child-switched.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipeable_switch_child (GtkHdySwipeable *self,
|
||
|
+ guint index,
|
||
|
+ gint64 duration)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPEABLE (self));
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_if_fail (iface->switch_child != NULL);
|
||
|
+
|
||
|
+ iface->switch_child (self, index, duration);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_emit_child_switched:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ * @index: the index of the child to switch to
|
||
|
+ * @duration: Animation duration in milliseconds
|
||
|
+ *
|
||
|
+ * Emits GtkHdySwipeable::child-switched signal. This should be called when the
|
||
|
+ * widget switches visible child widget.
|
||
|
+ *
|
||
|
+ * @duration can be 0 if the child is switched without animation.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipeable_emit_child_switched (GtkHdySwipeable *self,
|
||
|
+ guint index,
|
||
|
+ gint64 duration)
|
||
|
+{
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPEABLE (self));
|
||
|
+
|
||
|
+ g_signal_emit (self, signals[SIGNAL_CHILD_SWITCHED], 0, index, duration);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_swipe_tracker:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ *
|
||
|
+ * Gets the #GtkHdySwipeTracker used by this swipeable widget.
|
||
|
+ *
|
||
|
+ * Returns: (transfer none): the swipe tracker
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+GtkHdySwipeTracker *
|
||
|
+gtk_hdy_swipeable_get_swipe_tracker (GtkHdySwipeable *self)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (self), NULL);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_val_if_fail (iface->get_swipe_tracker != NULL, NULL);
|
||
|
+
|
||
|
+ return iface->get_swipe_tracker (self);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_distance:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ *
|
||
|
+ * Gets the swipe distance of @self. This corresponds to how many pixels
|
||
|
+ * 1 unit represents.
|
||
|
+ *
|
||
|
+ * Returns: the swipe distance in pixels
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gdouble
|
||
|
+gtk_hdy_swipeable_get_distance (GtkHdySwipeable *self)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (self), 0);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_val_if_fail (iface->get_distance != NULL, 0);
|
||
|
+
|
||
|
+ return iface->get_distance (self);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_snap_points: (virtual get_snap_points)
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ * @n_snap_points: (out): location to return the number of the snap points
|
||
|
+ *
|
||
|
+ * Gets the snap points of @self. Each snap point represents a progress value
|
||
|
+ * that is considered acceptable to end the swipe on.
|
||
|
+ *
|
||
|
+ * Returns: (array length=n_snap_points) (transfer full): the snap points of
|
||
|
+ * @self. The array must be freed with g_free().
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gdouble *
|
||
|
+gtk_hdy_swipeable_get_snap_points (GtkHdySwipeable *self,
|
||
|
+ gint *n_snap_points)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (self), NULL);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_val_if_fail (iface->get_snap_points != NULL, NULL);
|
||
|
+
|
||
|
+ return iface->get_snap_points (self, n_snap_points);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_progress:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ *
|
||
|
+ * Gets the current progress of @self
|
||
|
+ *
|
||
|
+ * Returns: the current progress, unitless
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gdouble
|
||
|
+gtk_hdy_swipeable_get_progress (GtkHdySwipeable *self)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (self), 0);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_val_if_fail (iface->get_progress != NULL, 0);
|
||
|
+
|
||
|
+ return iface->get_progress (self);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_cancel_progress:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ *
|
||
|
+ * Gets the progress @self will snap back to after the gesture is canceled.
|
||
|
+ *
|
||
|
+ * Returns: the cancel progress, unitless
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+gdouble
|
||
|
+gtk_hdy_swipeable_get_cancel_progress (GtkHdySwipeable *self)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_val_if_fail (GTK_IS_HDY_SWIPEABLE (self), 0);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+ g_return_val_if_fail (iface->get_cancel_progress != NULL, 0);
|
||
|
+
|
||
|
+ return iface->get_cancel_progress (self);
|
||
|
+}
|
||
|
+
|
||
|
+/**
|
||
|
+ * gtk_hdy_swipeable_get_swipe_area:
|
||
|
+ * @self: a #GtkHdySwipeable
|
||
|
+ * @navigation_direction: the direction of the swipe
|
||
|
+ * @is_drag: whether the swipe is caused by a dragging gesture
|
||
|
+ * @rect: (out): a pointer to a #GdkRectangle to store the swipe area
|
||
|
+ *
|
||
|
+ * Gets the area @self can start a swipe from for the given direction and
|
||
|
+ * gesture type.
|
||
|
+ * This can be used to restrict swipes to only be possible from a certain area,
|
||
|
+ * for example, to only allow edge swipes, or to have a draggable element and
|
||
|
+ * ignore swipes elsewhere.
|
||
|
+ *
|
||
|
+ * Swipe area is only considered for direct swipes (as in, not initiated by
|
||
|
+ * #GtkHdySwipeGroup).
|
||
|
+ *
|
||
|
+ * If not implemented, the default implementation returns the allocation of
|
||
|
+ * @self, allowing swipes from anywhere.
|
||
|
+ *
|
||
|
+ * Since: 1.0
|
||
|
+ */
|
||
|
+void
|
||
|
+gtk_hdy_swipeable_get_swipe_area (GtkHdySwipeable *self,
|
||
|
+ GtkHdyNavigationDirection navigation_direction,
|
||
|
+ gboolean is_drag,
|
||
|
+ GdkRectangle *rect)
|
||
|
+{
|
||
|
+ GtkHdySwipeableInterface *iface;
|
||
|
+
|
||
|
+ g_return_if_fail (GTK_IS_HDY_SWIPEABLE (self));
|
||
|
+ g_return_if_fail (rect != NULL);
|
||
|
+
|
||
|
+ iface = GTK_HDY_SWIPEABLE_GET_IFACE (self);
|
||
|
+
|
||
|
+ if (iface->get_swipe_area) {
|
||
|
+ iface->get_swipe_area (self, navigation_direction, is_drag, rect);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ rect->x = 0;
|
||
|
+ rect->y = 0;
|
||
|
+ rect->width = gtk_widget_get_allocated_width (GTK_WIDGET (self));
|
||
|
+ rect->height = gtk_widget_get_allocated_height (GTK_WIDGET (self));
|
||
|
+}
|
||
|
diff --git a/gtk/meson.build b/gtk/meson.build
|
||
|
index 7407f5b..ae63ff0 100644
|
||
|
--- a/gtk/meson.build
|
||
|
+++ b/gtk/meson.build
|
||
|
@@ -388,6 +388,8 @@ gtk_sources = files(
|
||
|
'hdy-navigation-direction.c',
|
||
|
'hdy-shadow-helper.c',
|
||
|
'hdy-squeezer.c',
|
||
|
+ 'hdy-swipeable.c',
|
||
|
+ 'hdy-swipe-tracker.c',
|
||
|
'hdy-view-switcher-bar.c',
|
||
|
'hdy-view-switcher-button.c',
|
||
|
'hdy-view-switcher.c',
|
||
|
@@ -405,6 +407,8 @@ gtk_private_type_headers = files(
|
||
|
'hdy-navigation-direction-private.h',
|
||
|
'hdy-shadow-helper-private.h',
|
||
|
'hdy-squeezer-private.h',
|
||
|
+ 'hdy-swipeable-private.h',
|
||
|
+ 'hdy-swipe-tracker-private.h',
|
||
|
'hdy-view-switcher-bar-private.h',
|
||
|
'hdy-view-switcher-button-private.h',
|
||
|
'hdy-view-switcher-private.h',
|