hdy-swipeable.c 6.83 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"

9
#include "hdy-swipeable.h"
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
10
11

/**
12
 * HdySwipeable:
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
13
 *
14
 * An interface for swipeable widgets.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
15
 *
16
17
 * The `HdySwipeable` interface is implemented by all swipeable widgets. They
 * can be synced using [class@SwipeGroup].
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
18
 *
19
20
21
 * See [class@SwipeTracker] for details about implementing it.
 *
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
22
23
24
25
26
 */

G_DEFINE_INTERFACE (HdySwipeable, hdy_swipeable, GTK_TYPE_WIDGET)

enum {
27
  SIGNAL_CHILD_SWITCHED,
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
28
29
30
31
32
33
34
35
36
  SIGNAL_LAST_SIGNAL,
};

static guint signals[SIGNAL_LAST_SIGNAL];

static void
hdy_swipeable_default_init (HdySwipeableInterface *iface)
{
  /**
37
   * HdySwipeable::child-switched:
38
   * @self: a swipeable
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
39
   * @index: the index of the child to switch to
40
   * @duration: animation duration, in milliseconds
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
41
   *
42
   * Emitted when the widget's visible child is changed.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
43
44
45
   *
   * @duration can be 0 if the child is switched without animation.
   *
46
   * This is used by [class@SwipeGroup], applications should not connect to it.
47
   *
48
   * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
49
   */
50
51
  signals[SIGNAL_CHILD_SWITCHED] =
    g_signal_new ("child-switched",
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
52
53
54
55
56
57
58
59
60
61
62
                  G_TYPE_FROM_INTERFACE (iface),
                  G_SIGNAL_RUN_FIRST,
                  0,
                  NULL, NULL, NULL,
                  G_TYPE_NONE,
                  2,
                  G_TYPE_UINT, G_TYPE_INT64);
}

/**
 * hdy_swipeable_switch_child:
63
 * @self: a swipeable
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
64
 * @index: the index of the child to switch to
65
 * @duration: animation duration, in milliseconds
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
66
 *
67
68
69
 * Switches to child with index @index.
 *
 * See [signal@Swipeable::child-switched].
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
70
 *
71
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
72
73
74
75
76
77
78
79
80
81
82
83
84
 */
void
hdy_swipeable_switch_child (HdySwipeable *self,
                            guint         index,
                            gint64        duration)
{
  HdySwipeableInterface *iface;

  g_return_if_fail (HDY_IS_SWIPEABLE (self));

  iface = HDY_SWIPEABLE_GET_IFACE (self);
  g_return_if_fail (iface->switch_child != NULL);

85
  iface->switch_child (self, index, duration);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
86
87
88
}

/**
89
 * hdy_swipeable_emit_child_switched:
90
 * @self: a swipeable
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
91
 * @index: the index of the child to switch to
92
93
94
 * @duration: animation duration, in milliseconds
 *
 * Emits [signal@Swipeable::child-switched] signal.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
95
 *
96
 * This should be called when the widget switches visible child widget.
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
97
98
99
 *
 * @duration can be 0 if the child is switched without animation.
 *
100
 * Since: 1.0
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
101
102
 */
void
103
104
105
hdy_swipeable_emit_child_switched (HdySwipeable *self,
                                   guint         index,
                                   gint64        duration)
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
106
107
108
{
  g_return_if_fail (HDY_IS_SWIPEABLE (self));

109
  g_signal_emit (self, signals[SIGNAL_CHILD_SWITCHED], 0, index, duration);
Alexander Mikhaylenko's avatar
Alexander Mikhaylenko committed
110
}
111

112
113
/**
 * hdy_swipeable_get_swipe_tracker:
114
 * @self: a swipeable
115
 *
116
 * Gets the [class@SwipeTracker] used by this swipeable widget.
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
 *
 * Returns: (transfer none): the swipe tracker
 *
 * Since: 1.0
 */
HdySwipeTracker *
hdy_swipeable_get_swipe_tracker (HdySwipeable *self)
{
  HdySwipeableInterface *iface;

  g_return_val_if_fail (HDY_IS_SWIPEABLE (self), NULL);

  iface = HDY_SWIPEABLE_GET_IFACE (self);
  g_return_val_if_fail (iface->get_swipe_tracker != NULL, NULL);

132
  return iface->get_swipe_tracker (self);
133
134
}

135
136
/**
 * hdy_swipeable_get_distance:
137
 * @self: a swipeable
138
 *
139
140
141
 * Gets the swipe distance of @self.
 *
 * This corresponds to how many pixels 1 unit represents.
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
 *
 * Returns: the swipe distance in pixels
 *
 * Since: 1.0
 */
gdouble
hdy_swipeable_get_distance (HdySwipeable *self)
{
  HdySwipeableInterface *iface;

  g_return_val_if_fail (HDY_IS_SWIPEABLE (self), 0);

  iface = HDY_SWIPEABLE_GET_IFACE (self);
  g_return_val_if_fail (iface->get_distance != NULL, 0);

157
  return iface->get_distance (self);
158
159
160
}

/**
161
 * hdy_swipeable_get_snap_points: (virtual get_snap_points)
162
 * @self: a swipeable
163
 * @n_snap_points: (out): location to return the number of the snap points
164
 *
165
166
167
168
 * Gets the snap points of @self.
 *
 * Each snap point represents a progress value that is considered acceptable to
 * end the swipe on.
169
 *
170
 * Returns: (array length=n_snap_points) (transfer full): the snap points
171
172
173
174
175
176
177
178
179
180
181
182
 *
 * Since: 1.0
 */
gdouble *
hdy_swipeable_get_snap_points (HdySwipeable *self,
                               gint         *n_snap_points)
{
  HdySwipeableInterface *iface;

  g_return_val_if_fail (HDY_IS_SWIPEABLE (self), NULL);

  iface = HDY_SWIPEABLE_GET_IFACE (self);
183
  g_return_val_if_fail (iface->get_snap_points != NULL, NULL);
184

185
  return iface->get_snap_points (self, n_snap_points);
186
187
188
189
}

/**
 * hdy_swipeable_get_progress:
190
 * @self: a swipeable
191
 *
192
 * Gets the current progress of @self.
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
 *
 * Returns: the current progress, unitless
 *
 * Since: 1.0
 */
gdouble
hdy_swipeable_get_progress (HdySwipeable *self)
{
  HdySwipeableInterface *iface;

  g_return_val_if_fail (HDY_IS_SWIPEABLE (self), 0);

  iface = HDY_SWIPEABLE_GET_IFACE (self);
  g_return_val_if_fail (iface->get_progress != NULL, 0);

208
  return iface->get_progress (self);
209
210
211
212
}

/**
 * hdy_swipeable_get_cancel_progress:
213
 * @self: a swipeable
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
 *
 * Gets the progress @self will snap back to after the gesture is canceled.
 *
 * Returns: the cancel progress, unitless
 *
 * Since: 1.0
 */
gdouble
hdy_swipeable_get_cancel_progress (HdySwipeable *self)
{
  HdySwipeableInterface *iface;

  g_return_val_if_fail (HDY_IS_SWIPEABLE (self), 0);

  iface = HDY_SWIPEABLE_GET_IFACE (self);
  g_return_val_if_fail (iface->get_cancel_progress != NULL, 0);

231
  return iface->get_cancel_progress (self);
232
}
233
234
235

/**
 * hdy_swipeable_get_swipe_area:
236
 * @self: a swipeable
237
238
 * @navigation_direction: the direction of the swipe
 * @is_drag: whether the swipe is caused by a dragging gesture
239
 * @rect: (out): a pointer to a rectangle to store the swipe area
240
 *
241
242
 * Gets the area @self can start a swipe from for the given direction and
 * gesture type.
243
 *
244
245
246
 * 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.
247
248
 *
 * Swipe area is only considered for direct swipes (as in, not initiated by
249
 * [class@SwipeGroup]).
250
251
252
253
254
255
256
 *
 * If not implemented, the default implementation returns the allocation of
 * @self, allowing swipes from anywhere.
 *
 * Since: 1.0
 */
void
257
258
259
260
hdy_swipeable_get_swipe_area (HdySwipeable           *self,
                              HdyNavigationDirection  navigation_direction,
                              gboolean                is_drag,
                              GdkRectangle           *rect)
261
262
263
264
265
266
267
268
269
{
  HdySwipeableInterface *iface;

  g_return_if_fail (HDY_IS_SWIPEABLE (self));
  g_return_if_fail (rect != NULL);

  iface = HDY_SWIPEABLE_GET_IFACE (self);

  if (iface->get_swipe_area) {
270
    iface->get_swipe_area (self, navigation_direction, is_drag, rect);
271
272
273
274
275
276
277
278
    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));
}