1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2017, Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author(s): Carlos Garnacho <carlosg@gnome.org> |
18 | */ |
19 | |
20 | /** |
21 | * GtkEventControllerScroll: |
22 | * |
23 | * `GtkEventControllerScroll` is an event controller that handles scroll |
24 | * events. |
25 | * |
26 | * It is capable of handling both discrete and continuous scroll |
27 | * events from mice or touchpads, abstracting them both with the |
28 | * [signal@Gtk.EventControllerScroll::scroll] signal. Deltas in |
29 | * the discrete case are multiples of 1. |
30 | * |
31 | * In the case of continuous scroll events, `GtkEventControllerScroll` |
32 | * encloses all [signal@Gtk.EventControllerScroll::scroll] emissions |
33 | * between two [signal@Gtk.EventControllerScroll::scroll-begin] and |
34 | * [signal@Gtk.EventControllerScroll::scroll-end] signals. |
35 | * |
36 | * The behavior of the event controller can be modified by the flags |
37 | * given at creation time, or modified at a later point through |
38 | * [method@Gtk.EventControllerScroll.set_flags] (e.g. because the scrolling |
39 | * conditions of the widget changed). |
40 | * |
41 | * The controller can be set up to emit motion for either/both vertical |
42 | * and horizontal scroll events through %GTK_EVENT_CONTROLLER_SCROLL_VERTICAL, |
43 | * %GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL and %GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES. |
44 | * If any axis is disabled, the respective [signal@Gtk.EventControllerScroll::scroll] |
45 | * delta will be 0. Vertical scroll events will be translated to horizontal |
46 | * motion for the devices incapable of horizontal scrolling. |
47 | * |
48 | * The event controller can also be forced to emit discrete events on all |
49 | * devices through %GTK_EVENT_CONTROLLER_SCROLL_DISCRETE. This can be used |
50 | * to implement discrete actions triggered through scroll events (e.g. |
51 | * switching across combobox options). |
52 | * |
53 | * The %GTK_EVENT_CONTROLLER_SCROLL_KINETIC flag toggles the emission of the |
54 | * [signal@Gtk.EventControllerScroll::decelerate] signal, emitted at the end |
55 | * of scrolling with two X/Y velocity arguments that are consistent with the |
56 | * motion that was received. |
57 | */ |
58 | #include "config.h" |
59 | |
60 | #include "gtkintl.h" |
61 | #include "gtkwidget.h" |
62 | #include "gtkeventcontrollerprivate.h" |
63 | #include "gtkeventcontrollerscroll.h" |
64 | #include "gtktypebuiltins.h" |
65 | #include "gtkmarshalers.h" |
66 | #include "gtkprivate.h" |
67 | |
68 | #define SCROLL_CAPTURE_THRESHOLD_MS 150 |
69 | #define HOLD_TIMEOUT_MS 50 |
70 | |
71 | typedef struct |
72 | { |
73 | double dx; |
74 | double dy; |
75 | guint32 evtime; |
76 | } ScrollHistoryElem; |
77 | |
78 | struct _GtkEventControllerScroll |
79 | { |
80 | GtkEventController parent_instance; |
81 | GtkEventControllerScrollFlags flags; |
82 | GArray *scroll_history; |
83 | |
84 | /* For discrete event coalescing */ |
85 | double cur_dx; |
86 | double cur_dy; |
87 | |
88 | guint hold_timeout_id; |
89 | guint active : 1; |
90 | }; |
91 | |
92 | struct _GtkEventControllerScrollClass |
93 | { |
94 | GtkEventControllerClass parent_class; |
95 | }; |
96 | |
97 | enum { |
98 | SCROLL_BEGIN, |
99 | SCROLL, |
100 | SCROLL_END, |
101 | DECELERATE, |
102 | N_SIGNALS |
103 | }; |
104 | |
105 | enum { |
106 | PROP_0, |
107 | PROP_FLAGS, |
108 | N_PROPS |
109 | }; |
110 | |
111 | static GParamSpec *pspecs[N_PROPS] = { NULL }; |
112 | static guint signals[N_SIGNALS] = { 0 }; |
113 | |
114 | G_DEFINE_TYPE (GtkEventControllerScroll, gtk_event_controller_scroll, |
115 | GTK_TYPE_EVENT_CONTROLLER) |
116 | |
117 | static void |
118 | scroll_history_push (GtkEventControllerScroll *scroll, |
119 | double delta_x, |
120 | double delta_y, |
121 | guint32 evtime) |
122 | { |
123 | ScrollHistoryElem new_item; |
124 | guint i; |
125 | |
126 | for (i = 0; i < scroll->scroll_history->len; i++) |
127 | { |
128 | ScrollHistoryElem *elem; |
129 | |
130 | elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i); |
131 | |
132 | if (elem->evtime >= evtime - SCROLL_CAPTURE_THRESHOLD_MS) |
133 | break; |
134 | } |
135 | |
136 | if (i > 0) |
137 | g_array_remove_range (array: scroll->scroll_history, index_: 0, length: i); |
138 | |
139 | new_item.dx = delta_x; |
140 | new_item.dy = delta_y; |
141 | new_item.evtime = evtime; |
142 | g_array_append_val (scroll->scroll_history, new_item); |
143 | } |
144 | |
145 | static void |
146 | scroll_history_reset (GtkEventControllerScroll *scroll) |
147 | { |
148 | if (scroll->scroll_history->len == 0) |
149 | return; |
150 | |
151 | g_array_remove_range (array: scroll->scroll_history, index_: 0, |
152 | length: scroll->scroll_history->len); |
153 | } |
154 | |
155 | static void |
156 | scroll_history_finish (GtkEventControllerScroll *scroll, |
157 | double *velocity_x, |
158 | double *velocity_y) |
159 | { |
160 | double accum_dx = 0, accum_dy = 0; |
161 | guint32 first = 0, last = 0; |
162 | guint i; |
163 | |
164 | *velocity_x = 0; |
165 | *velocity_y = 0; |
166 | |
167 | if (scroll->scroll_history->len == 0) |
168 | return; |
169 | |
170 | for (i = 0; i < scroll->scroll_history->len; i++) |
171 | { |
172 | ScrollHistoryElem *elem; |
173 | |
174 | elem = &g_array_index (scroll->scroll_history, ScrollHistoryElem, i); |
175 | accum_dx += elem->dx; |
176 | accum_dy += elem->dy; |
177 | last = elem->evtime; |
178 | |
179 | if (i == 0) |
180 | first = elem->evtime; |
181 | } |
182 | |
183 | if (last != first) |
184 | { |
185 | *velocity_x = (accum_dx * 1000) / (last - first); |
186 | *velocity_y = (accum_dy * 1000) / (last - first); |
187 | } |
188 | |
189 | scroll_history_reset (scroll); |
190 | } |
191 | |
192 | static void |
193 | gtk_event_controller_scroll_finalize (GObject *object) |
194 | { |
195 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); |
196 | |
197 | g_array_unref (array: scroll->scroll_history); |
198 | g_clear_handle_id (&scroll->hold_timeout_id, g_source_remove); |
199 | |
200 | G_OBJECT_CLASS (gtk_event_controller_scroll_parent_class)->finalize (object); |
201 | } |
202 | |
203 | static void |
204 | gtk_event_controller_scroll_set_property (GObject *object, |
205 | guint prop_id, |
206 | const GValue *value, |
207 | GParamSpec *pspec) |
208 | { |
209 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); |
210 | |
211 | switch (prop_id) |
212 | { |
213 | case PROP_FLAGS: |
214 | gtk_event_controller_scroll_set_flags (scroll, flags: g_value_get_flags (value)); |
215 | break; |
216 | default: |
217 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
218 | break; |
219 | } |
220 | } |
221 | |
222 | static void |
223 | gtk_event_controller_scroll_get_property (GObject *object, |
224 | guint prop_id, |
225 | GValue *value, |
226 | GParamSpec *pspec) |
227 | { |
228 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (object); |
229 | |
230 | switch (prop_id) |
231 | { |
232 | case PROP_FLAGS: |
233 | g_value_set_flags (value, v_flags: scroll->flags); |
234 | break; |
235 | default: |
236 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
237 | break; |
238 | } |
239 | } |
240 | |
241 | static void |
242 | gtk_event_controller_scroll_begin (GtkEventController *controller) |
243 | { |
244 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); |
245 | |
246 | if (scroll->active) |
247 | return; |
248 | |
249 | g_signal_emit (instance: controller, signal_id: signals[SCROLL_BEGIN], detail: 0); |
250 | scroll_history_reset (scroll); |
251 | scroll->active = TRUE; |
252 | } |
253 | |
254 | static void |
255 | gtk_event_controller_scroll_end (GtkEventController *controller) |
256 | { |
257 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); |
258 | |
259 | if (!scroll->active) |
260 | return; |
261 | |
262 | g_signal_emit (instance: controller, signal_id: signals[SCROLL_END], detail: 0); |
263 | scroll->active = FALSE; |
264 | |
265 | if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC) |
266 | { |
267 | double vel_x, vel_y; |
268 | |
269 | scroll_history_finish (scroll, velocity_x: &vel_x, velocity_y: &vel_y); |
270 | g_signal_emit (instance: controller, signal_id: signals[DECELERATE], detail: 0, vel_x, vel_y); |
271 | } |
272 | } |
273 | |
274 | static gboolean |
275 | gtk_event_controller_scroll_hold_timeout (gpointer user_data) |
276 | { |
277 | GtkEventController *controller; |
278 | GtkEventControllerScroll *scroll; |
279 | |
280 | controller = user_data; |
281 | scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); |
282 | |
283 | gtk_event_controller_scroll_end (controller); |
284 | scroll->hold_timeout_id = 0; |
285 | |
286 | return G_SOURCE_REMOVE; |
287 | } |
288 | |
289 | static gboolean |
290 | gtk_event_controller_scroll_handle_hold_event (GtkEventController *controller, |
291 | GdkEvent *event) |
292 | { |
293 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); |
294 | GdkTouchpadGesturePhase phase; |
295 | guint n_fingers = 0; |
296 | |
297 | if (gdk_event_get_event_type (event) != GDK_TOUCHPAD_HOLD) |
298 | return GDK_EVENT_PROPAGATE; |
299 | |
300 | n_fingers = gdk_touchpad_event_get_n_fingers (event); |
301 | if (n_fingers != 1 && n_fingers != 2) |
302 | return GDK_EVENT_PROPAGATE; |
303 | |
304 | if (scroll->hold_timeout_id != 0) |
305 | return GDK_EVENT_PROPAGATE; |
306 | |
307 | phase = gdk_touchpad_event_get_gesture_phase (event); |
308 | |
309 | switch (phase) |
310 | { |
311 | case GDK_TOUCHPAD_GESTURE_PHASE_BEGIN: |
312 | gtk_event_controller_scroll_begin (controller); |
313 | break; |
314 | |
315 | case GDK_TOUCHPAD_GESTURE_PHASE_END: |
316 | gtk_event_controller_scroll_end (controller); |
317 | break; |
318 | |
319 | case GDK_TOUCHPAD_GESTURE_PHASE_CANCEL: |
320 | if (scroll->hold_timeout_id == 0) |
321 | { |
322 | scroll->hold_timeout_id = |
323 | g_timeout_add (HOLD_TIMEOUT_MS, |
324 | function: gtk_event_controller_scroll_hold_timeout, |
325 | data: controller); |
326 | } |
327 | break; |
328 | |
329 | case GDK_TOUCHPAD_GESTURE_PHASE_UPDATE: |
330 | default: |
331 | break; |
332 | } |
333 | |
334 | return GDK_EVENT_PROPAGATE; |
335 | } |
336 | |
337 | static gboolean |
338 | gtk_event_controller_scroll_handle_event (GtkEventController *controller, |
339 | GdkEvent *event, |
340 | double x, |
341 | double y) |
342 | { |
343 | GtkEventControllerScroll *scroll = GTK_EVENT_CONTROLLER_SCROLL (controller); |
344 | GdkScrollDirection direction = GDK_SCROLL_SMOOTH; |
345 | double dx = 0, dy = 0; |
346 | gboolean handled = GDK_EVENT_PROPAGATE; |
347 | GdkEventType event_type; |
348 | |
349 | event_type = gdk_event_get_event_type (event); |
350 | |
351 | if (event_type == GDK_TOUCHPAD_HOLD) |
352 | return gtk_event_controller_scroll_handle_hold_event (controller, event); |
353 | |
354 | if (event_type != GDK_SCROLL) |
355 | return FALSE; |
356 | |
357 | if ((scroll->flags & (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | |
358 | GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL)) == 0) |
359 | return FALSE; |
360 | |
361 | g_clear_handle_id (&scroll->hold_timeout_id, g_source_remove); |
362 | |
363 | /* FIXME: Handle device changes */ |
364 | direction = gdk_scroll_event_get_direction (event); |
365 | if (direction == GDK_SCROLL_SMOOTH) |
366 | { |
367 | gdk_scroll_event_get_deltas (event, delta_x: &dx, delta_y: &dy); |
368 | gtk_event_controller_scroll_begin (controller); |
369 | |
370 | if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0) |
371 | dy = 0; |
372 | if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0) |
373 | dx = 0; |
374 | |
375 | if (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_DISCRETE) |
376 | { |
377 | int steps; |
378 | |
379 | scroll->cur_dx += dx; |
380 | scroll->cur_dy += dy; |
381 | dx = dy = 0; |
382 | |
383 | if (ABS (scroll->cur_dx) >= 1) |
384 | { |
385 | steps = trunc (x: scroll->cur_dx); |
386 | scroll->cur_dx -= steps; |
387 | dx = steps; |
388 | } |
389 | |
390 | if (ABS (scroll->cur_dy) >= 1) |
391 | { |
392 | steps = trunc (x: scroll->cur_dy); |
393 | scroll->cur_dy -= steps; |
394 | dy = steps; |
395 | } |
396 | } |
397 | } |
398 | else |
399 | { |
400 | switch (direction) |
401 | { |
402 | case GDK_SCROLL_UP: |
403 | dy -= 1; |
404 | break; |
405 | case GDK_SCROLL_DOWN: |
406 | dy += 1; |
407 | break; |
408 | case GDK_SCROLL_LEFT: |
409 | dx -= 1; |
410 | break; |
411 | case GDK_SCROLL_RIGHT: |
412 | dx += 1; |
413 | break; |
414 | case GDK_SCROLL_SMOOTH: |
415 | default: |
416 | g_assert_not_reached (); |
417 | break; |
418 | } |
419 | |
420 | if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_VERTICAL) == 0) |
421 | dy = 0; |
422 | if ((scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_HORIZONTAL) == 0) |
423 | dx = 0; |
424 | } |
425 | |
426 | if (dx != 0 || dy != 0) |
427 | g_signal_emit (instance: controller, signal_id: signals[SCROLL], detail: 0, dx, dy, &handled); |
428 | else if (direction == GDK_SCROLL_SMOOTH && |
429 | (scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_DISCRETE) != 0) |
430 | handled = scroll->active; |
431 | |
432 | if (direction == GDK_SCROLL_SMOOTH && |
433 | scroll->flags & GTK_EVENT_CONTROLLER_SCROLL_KINETIC) |
434 | scroll_history_push (scroll, delta_x: dx, delta_y: dy, evtime: gdk_event_get_time (event)); |
435 | |
436 | if (scroll->active && gdk_scroll_event_is_stop (event)) |
437 | { |
438 | gtk_event_controller_scroll_end (controller); |
439 | handled = FALSE; |
440 | } |
441 | |
442 | return handled; |
443 | } |
444 | |
445 | static void |
446 | gtk_event_controller_scroll_class_init (GtkEventControllerScrollClass *klass) |
447 | { |
448 | GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); |
449 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
450 | |
451 | object_class->finalize = gtk_event_controller_scroll_finalize; |
452 | object_class->set_property = gtk_event_controller_scroll_set_property; |
453 | object_class->get_property = gtk_event_controller_scroll_get_property; |
454 | |
455 | controller_class->handle_event = gtk_event_controller_scroll_handle_event; |
456 | |
457 | /** |
458 | * GtkEventControllerScroll:flags: (attributes org.gtk.Property.get=gtk_event_controller_scroll_get_flags org.gtk.Property.set=gtk_event_controller_scroll_set_flags) |
459 | * |
460 | * The flags affecting event controller behavior. |
461 | */ |
462 | pspecs[PROP_FLAGS] = |
463 | g_param_spec_flags (name: "flags" , |
464 | P_("Flags" ), |
465 | P_("Flags" ), |
466 | flags_type: GTK_TYPE_EVENT_CONTROLLER_SCROLL_FLAGS, |
467 | default_value: GTK_EVENT_CONTROLLER_SCROLL_NONE, |
468 | GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); |
469 | |
470 | /** |
471 | * GtkEventControllerScroll::scroll-begin: |
472 | * @controller: The object that received the signal |
473 | * |
474 | * Signals that a new scrolling operation has begun. |
475 | * |
476 | * It will only be emitted on devices capable of it. |
477 | */ |
478 | signals[SCROLL_BEGIN] = |
479 | g_signal_new (I_("scroll-begin" ), |
480 | GTK_TYPE_EVENT_CONTROLLER_SCROLL, |
481 | signal_flags: G_SIGNAL_RUN_FIRST, |
482 | class_offset: 0, NULL, NULL, |
483 | NULL, |
484 | G_TYPE_NONE, n_params: 0); |
485 | |
486 | /** |
487 | * GtkEventControllerScroll::scroll: |
488 | * @controller: The object that received the signal |
489 | * @dx: X delta |
490 | * @dy: Y delta |
491 | * |
492 | * Signals that the widget should scroll by the |
493 | * amount specified by @dx and @dy. |
494 | * |
495 | * Returns: %TRUE if the scroll event was handled, |
496 | * %FALSE otherwise. |
497 | */ |
498 | signals[SCROLL] = |
499 | g_signal_new (I_("scroll" ), |
500 | GTK_TYPE_EVENT_CONTROLLER_SCROLL, |
501 | signal_flags: G_SIGNAL_RUN_LAST, |
502 | class_offset: 0, NULL, NULL, |
503 | c_marshaller: _gtk_marshal_BOOLEAN__DOUBLE_DOUBLE, |
504 | G_TYPE_BOOLEAN, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
505 | g_signal_set_va_marshaller (signal_id: signals[SCROLL], |
506 | G_TYPE_FROM_CLASS (klass), |
507 | va_marshaller: _gtk_marshal_BOOLEAN__DOUBLE_DOUBLEv); |
508 | |
509 | /** |
510 | * GtkEventControllerScroll::scroll-end: |
511 | * @controller: The object that received the signal |
512 | * |
513 | * Signals that a scrolling operation has finished. |
514 | * |
515 | * It will only be emitted on devices capable of it. |
516 | */ |
517 | signals[SCROLL_END] = |
518 | g_signal_new (I_("scroll-end" ), |
519 | GTK_TYPE_EVENT_CONTROLLER_SCROLL, |
520 | signal_flags: G_SIGNAL_RUN_FIRST, |
521 | class_offset: 0, NULL, NULL, |
522 | NULL, |
523 | G_TYPE_NONE, n_params: 0); |
524 | |
525 | /** |
526 | * GtkEventControllerScroll::decelerate: |
527 | * @controller: The object that received the signal |
528 | * @vel_x: X velocity |
529 | * @vel_y: Y velocity |
530 | * |
531 | * Emitted after scroll is finished if the |
532 | * %GTK_EVENT_CONTROLLER_SCROLL_KINETIC flag is set. |
533 | * |
534 | * @vel_x and @vel_y express the initial velocity that was |
535 | * imprinted by the scroll events. @vel_x and @vel_y are expressed in |
536 | * pixels/ms. |
537 | */ |
538 | signals[DECELERATE] = |
539 | g_signal_new (I_("decelerate" ), |
540 | GTK_TYPE_EVENT_CONTROLLER_SCROLL, |
541 | signal_flags: G_SIGNAL_RUN_FIRST, |
542 | class_offset: 0, NULL, NULL, |
543 | c_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLE, |
544 | G_TYPE_NONE, n_params: 2, G_TYPE_DOUBLE, G_TYPE_DOUBLE); |
545 | g_signal_set_va_marshaller (signal_id: signals[DECELERATE], |
546 | G_TYPE_FROM_CLASS (klass), |
547 | va_marshaller: _gtk_marshal_VOID__DOUBLE_DOUBLEv); |
548 | |
549 | g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPS, pspecs); |
550 | } |
551 | |
552 | static void |
553 | gtk_event_controller_scroll_init (GtkEventControllerScroll *scroll) |
554 | { |
555 | scroll->scroll_history = g_array_new (FALSE, FALSE, |
556 | element_size: sizeof (ScrollHistoryElem)); |
557 | scroll->hold_timeout_id = 0; |
558 | } |
559 | |
560 | /** |
561 | * gtk_event_controller_scroll_new: |
562 | * @flags: flags affecting the controller behavior |
563 | * |
564 | * Creates a new event controller that will handle scroll events. |
565 | * |
566 | * Returns: a new `GtkEventControllerScroll` |
567 | */ |
568 | GtkEventController * |
569 | gtk_event_controller_scroll_new (GtkEventControllerScrollFlags flags) |
570 | { |
571 | return g_object_new (GTK_TYPE_EVENT_CONTROLLER_SCROLL, |
572 | first_property_name: "flags" , flags, |
573 | NULL); |
574 | } |
575 | |
576 | /** |
577 | * gtk_event_controller_scroll_set_flags: (attributes org.gtk.Method.set_property=flags) |
578 | * @scroll: a `GtkEventControllerScroll` |
579 | * @flags: flags affecting the controller behavior |
580 | * |
581 | * Sets the flags conditioning scroll controller behavior. |
582 | */ |
583 | void |
584 | gtk_event_controller_scroll_set_flags (GtkEventControllerScroll *scroll, |
585 | GtkEventControllerScrollFlags flags) |
586 | { |
587 | g_return_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll)); |
588 | |
589 | if (scroll->flags == flags) |
590 | return; |
591 | |
592 | scroll->flags = flags; |
593 | g_object_notify_by_pspec (G_OBJECT (scroll), pspec: pspecs[PROP_FLAGS]); |
594 | } |
595 | |
596 | /** |
597 | * gtk_event_controller_scroll_get_flags: (attributes org.gtk.Method.get_property=flags) |
598 | * @scroll: a `GtkEventControllerScroll` |
599 | * |
600 | * Gets the flags conditioning the scroll controller behavior. |
601 | * |
602 | * Returns: the controller flags. |
603 | */ |
604 | GtkEventControllerScrollFlags |
605 | gtk_event_controller_scroll_get_flags (GtkEventControllerScroll *scroll) |
606 | { |
607 | g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_SCROLL (scroll), |
608 | GTK_EVENT_CONTROLLER_SCROLL_NONE); |
609 | |
610 | return scroll->flags; |
611 | } |
612 | |