1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 // generated automatically - do not change 21 // find conversion definition on APILookup.txt 22 // implement new conversion functionalities on the wrap.utils pakage 23 24 25 module gtk.Gesture; 26 27 private import gdk.Device; 28 private import gdk.Event; 29 private import gdk.Window; 30 private import glib.ListG; 31 private import gobject.ObjectG; 32 private import gobject.Signals; 33 private import gtk.EventController; 34 public import gtkc.gdktypes; 35 private import gtkc.gtk; 36 public import gtkc.gtktypes; 37 private import std.algorithm; 38 39 40 /** 41 * #GtkGesture is the base object for gesture recognition, although this 42 * object is quite generalized to serve as a base for multi-touch gestures, 43 * it is suitable to implement single-touch and pointer-based gestures (using 44 * the special %NULL #GdkEventSequence value for these). 45 * 46 * The number of touches that a #GtkGesture need to be recognized is controlled 47 * by the #GtkGesture:n-points property, if a gesture is keeping track of less 48 * or more than that number of sequences, it won't check wether the gesture 49 * is recognized. 50 * 51 * As soon as the gesture has the expected number of touches, the gesture will 52 * run the #GtkGesture::check signal regularly on input events until the gesture 53 * is recognized, the criteria to consider a gesture as "recognized" is left to 54 * #GtkGesture subclasses. 55 * 56 * A recognized gesture will then emit the following signals: 57 * - #GtkGesture::begin when the gesture is recognized. 58 * - A number of #GtkGesture::update, whenever an input event is processed. 59 * - #GtkGesture::end when the gesture is no longer recognized. 60 * 61 * ## Event propagation 62 * 63 * In order to receive events, a gesture needs to either set a propagation phase 64 * through gtk_event_controller_set_propagation_phase(), or feed those manually 65 * through gtk_event_controller_handle_event(). 66 * 67 * In the capture phase, events are propagated from the toplevel down to the 68 * target widget, and gestures that are attached to containers above the widget 69 * get a chance to interact with the event before it reaches the target. 70 * 71 * After the capture phase, GTK+ emits the traditional #GtkWidget::button-press-event, 72 * #GtkWidget::button-release-event, #GtkWidget::touch-event, etc signals. Gestures 73 * with the %GTK_PHASE_TARGET phase are fed events from the default #GtkWidget::event 74 * handlers. 75 * 76 * In the bubble phase, events are propagated up from the target widget to the 77 * toplevel, and gestures that are attached to containers above the widget get 78 * a chance to interact with events that have not been handled yet. 79 * 80 * ## States of a sequence # {#touch-sequence-states} 81 * 82 * Whenever input interaction happens, a single event may trigger a cascade of 83 * #GtkGestures, both across the parents of the widget receiving the event and 84 * in parallel within an individual widget. It is a responsibility of the 85 * widgets using those gestures to set the state of touch sequences accordingly 86 * in order to enable cooperation of gestures around the #GdkEventSequences 87 * triggering those. 88 * 89 * Within a widget, gestures can be grouped through gtk_gesture_group(), 90 * grouped gestures synchronize the state of sequences, so calling 91 * gtk_gesture_set_sequence_state() on one will effectively propagate 92 * the state throughout the group. 93 * 94 * By default, all sequences start out in the #GTK_EVENT_SEQUENCE_NONE state, 95 * sequences in this state trigger the gesture event handler, but event 96 * propagation will continue unstopped by gestures. 97 * 98 * If a sequence enters into the #GTK_EVENT_SEQUENCE_DENIED state, the gesture 99 * group will effectively ignore the sequence, letting events go unstopped 100 * through the gesture, but the "slot" will still remain occupied while 101 * the touch is active. 102 * 103 * If a sequence enters in the #GTK_EVENT_SEQUENCE_CLAIMED state, the gesture 104 * group will grab all interaction on the sequence, by: 105 * - Setting the same sequence to #GTK_EVENT_SEQUENCE_DENIED on every other gesture 106 * group within the widget, and every gesture on parent widgets in the propagation 107 * chain. 108 * - calling #GtkGesture::cancel on every gesture in widgets underneath in the 109 * propagation chain. 110 * - Stopping event propagation after the gesture group handles the event. 111 * 112 * Note: if a sequence is set early to #GTK_EVENT_SEQUENCE_CLAIMED on 113 * #GDK_TOUCH_BEGIN/#GDK_BUTTON_PRESS (so those events are captured before 114 * reaching the event widget, this implies #GTK_PHASE_CAPTURE), one similar 115 * event will emulated if the sequence changes to #GTK_EVENT_SEQUENCE_DENIED. 116 * This way event coherence is preserved before event propagation is unstopped 117 * again. 118 * 119 * Sequence states can't be changed freely, see gtk_gesture_set_sequence_state() 120 * to know about the possible lifetimes of a #GdkEventSequence. 121 * 122 * ## Touchpad gestures 123 * 124 * On the platforms that support it, #GtkGesture will handle transparently 125 * touchpad gesture events. The only precautions users of #GtkGesture should do 126 * to enable this support are: 127 * - Enabling %GDK_TOUCHPAD_GESTURE_MASK on their #GdkWindows 128 * - If the gesture has %GTK_PHASE_NONE, ensuring events of type 129 * %GDK_TOUCHPAD_SWIPE and %GDK_TOUCHPAD_PINCH are handled by the #GtkGesture 130 */ 131 public class Gesture : EventController 132 { 133 /** the main Gtk struct */ 134 protected GtkGesture* gtkGesture; 135 136 /** Get the main Gtk struct */ 137 public GtkGesture* getGestureStruct() 138 { 139 return gtkGesture; 140 } 141 142 /** the main Gtk struct as a void* */ 143 protected override void* getStruct() 144 { 145 return cast(void*)gtkGesture; 146 } 147 148 protected override void setStruct(GObject* obj) 149 { 150 gtkGesture = cast(GtkGesture*)obj; 151 super.setStruct(obj); 152 } 153 154 /** 155 * Sets our main struct and passes it to the parent class. 156 */ 157 public this (GtkGesture* gtkGesture, bool ownedRef = false) 158 { 159 this.gtkGesture = gtkGesture; 160 super(cast(GtkEventController*)gtkGesture, ownedRef); 161 } 162 163 164 /** */ 165 public static GType getType() 166 { 167 return gtk_gesture_get_type(); 168 } 169 170 /** 171 * If there are touch sequences being currently handled by @gesture, 172 * this function returns %TRUE and fills in @rect with the bounding 173 * box containing all active touches. Otherwise, %FALSE will be 174 * returned. 175 * 176 * Note: This function will yield unexpected results on touchpad 177 * gestures. Since there is no correlation between physical and 178 * pixel distances, these will look as if constrained in an 179 * infinitely small area, @rect width and height will thus be 0 180 * regardless of the number of touchpoints. 181 * 182 * Params: 183 * rect = bounding box containing all active touches. 184 * 185 * Return: %TRUE if there are active touches, %FALSE otherwise 186 * 187 * Since: 3.14 188 */ 189 public bool getBoundingBox(out GdkRectangle rect) 190 { 191 return gtk_gesture_get_bounding_box(gtkGesture, &rect) != 0; 192 } 193 194 /** 195 * If there are touch sequences being currently handled by @gesture, 196 * this function returns %TRUE and fills in @x and @y with the center 197 * of the bounding box containing all active touches. Otherwise, %FALSE 198 * will be returned. 199 * 200 * Params: 201 * x = X coordinate for the bounding box center 202 * y = Y coordinate for the bounding box center 203 * 204 * Return: %FALSE if no active touches are present, %TRUE otherwise 205 * 206 * Since: 3.14 207 */ 208 public bool getBoundingBoxCenter(out double x, out double y) 209 { 210 return gtk_gesture_get_bounding_box_center(gtkGesture, &x, &y) != 0; 211 } 212 213 /** 214 * Returns the master #GdkDevice that is currently operating 215 * on @gesture, or %NULL if the gesture is not being interacted. 216 * 217 * Return: a #GdkDevice, or %NULL 218 * 219 * Since: 3.14 220 */ 221 public Device getDevice() 222 { 223 auto p = gtk_gesture_get_device(gtkGesture); 224 225 if(p is null) 226 { 227 return null; 228 } 229 230 return ObjectG.getDObject!(Device)(cast(GdkDevice*) p); 231 } 232 233 /** 234 * Returns all gestures in the group of @gesture 235 * 236 * Return: The list 237 * of #GtkGestures, free with g_list_free() 238 * 239 * Since: 3.14 240 */ 241 public ListG getGroup() 242 { 243 auto p = gtk_gesture_get_group(gtkGesture); 244 245 if(p is null) 246 { 247 return null; 248 } 249 250 return new ListG(cast(GList*) p); 251 } 252 253 /** 254 * Returns the last event that was processed for @sequence. 255 * 256 * Note that the returned pointer is only valid as long as the @sequence 257 * is still interpreted by the @gesture. If in doubt, you should make 258 * a copy of the event. 259 * 260 * Params: 261 * sequence = a #GdkEventSequence 262 * 263 * Return: The last event from @sequence 264 */ 265 public Event getLastEvent(GdkEventSequence* sequence) 266 { 267 auto p = gtk_gesture_get_last_event(gtkGesture, sequence); 268 269 if(p is null) 270 { 271 return null; 272 } 273 274 return ObjectG.getDObject!(Event)(cast(GdkEvent*) p); 275 } 276 277 /** 278 * Returns the #GdkEventSequence that was last updated on @gesture. 279 * 280 * Return: The last updated sequence 281 * 282 * Since: 3.14 283 */ 284 public GdkEventSequence* getLastUpdatedSequence() 285 { 286 return gtk_gesture_get_last_updated_sequence(gtkGesture); 287 } 288 289 /** 290 * If @sequence is currently being interpreted by @gesture, this 291 * function returns %TRUE and fills in @x and @y with the last coordinates 292 * stored for that event sequence. The coordinates are always relative to the 293 * widget allocation. 294 * 295 * Params: 296 * sequence = a #GdkEventSequence, or %NULL for pointer events 297 * x = return location for X axis of the sequence coordinates 298 * y = return location for Y axis of the sequence coordinates 299 * 300 * Return: %TRUE if @sequence is currently interpreted 301 * 302 * Since: 3.14 303 */ 304 public bool getPoint(GdkEventSequence* sequence, out double x, out double y) 305 { 306 return gtk_gesture_get_point(gtkGesture, sequence, &x, &y) != 0; 307 } 308 309 /** 310 * Returns the @sequence state, as seen by @gesture. 311 * 312 * Params: 313 * sequence = a #GdkEventSequence 314 * 315 * Return: The sequence state in @gesture 316 * 317 * Since: 3.14 318 */ 319 public GtkEventSequenceState getSequenceState(GdkEventSequence* sequence) 320 { 321 return gtk_gesture_get_sequence_state(gtkGesture, sequence); 322 } 323 324 /** 325 * Returns the list of #GdkEventSequences currently being interpreted 326 * by @gesture. 327 * 328 * Return: A list 329 * of #GdkEventSequences, the list elements are owned by GTK+ 330 * and must not be freed or modified, the list itself must be deleted 331 * through g_list_free() 332 * 333 * Since: 3.14 334 */ 335 public ListG getSequences() 336 { 337 auto p = gtk_gesture_get_sequences(gtkGesture); 338 339 if(p is null) 340 { 341 return null; 342 } 343 344 return new ListG(cast(GList*) p); 345 } 346 347 /** 348 * Returns the user-defined window that receives the events 349 * handled by @gesture. See gtk_gesture_set_window() for more 350 * information. 351 * 352 * Return: the user defined window, or %NULL if none 353 * 354 * Since: 3.14 355 */ 356 public Window getWindow() 357 { 358 auto p = gtk_gesture_get_window(gtkGesture); 359 360 if(p is null) 361 { 362 return null; 363 } 364 365 return ObjectG.getDObject!(Window)(cast(GdkWindow*) p); 366 } 367 368 /** 369 * Adds @gesture to the same group than @group_gesture. Gestures 370 * are by default isolated in their own groups. 371 * 372 * When gestures are grouped, the state of #GdkEventSequences 373 * is kept in sync for all of those, so calling gtk_gesture_set_sequence_state(), 374 * on one will transfer the same value to the others. 375 * 376 * Groups also perform an "implicit grabbing" of sequences, if a 377 * #GdkEventSequence state is set to #GTK_EVENT_SEQUENCE_CLAIMED on one group, 378 * every other gesture group attached to the same #GtkWidget will switch the 379 * state for that sequence to #GTK_EVENT_SEQUENCE_DENIED. 380 * 381 * Params: 382 * gesture = a #GtkGesture 383 * 384 * Since: 3.14 385 */ 386 public void group(Gesture gesture) 387 { 388 gtk_gesture_group(gtkGesture, (gesture is null) ? null : gesture.getGestureStruct()); 389 } 390 391 /** 392 * Returns %TRUE if @gesture is currently handling events corresponding to 393 * @sequence. 394 * 395 * Params: 396 * sequence = a #GdkEventSequence 397 * 398 * Return: %TRUE if @gesture is handling @sequence 399 * 400 * Since: 3.14 401 */ 402 public bool handlesSequence(GdkEventSequence* sequence) 403 { 404 return gtk_gesture_handles_sequence(gtkGesture, sequence) != 0; 405 } 406 407 /** 408 * Returns %TRUE if the gesture is currently active. 409 * A gesture is active meanwhile there are touch sequences 410 * interacting with it. 411 * 412 * Return: %TRUE if gesture is active 413 * 414 * Since: 3.14 415 */ 416 public bool isActive() 417 { 418 return gtk_gesture_is_active(gtkGesture) != 0; 419 } 420 421 /** 422 * Returns %TRUE if both gestures pertain to the same group. 423 * 424 * Params: 425 * other = another #GtkGesture 426 * 427 * Return: whether the gestures are grouped 428 * 429 * Since: 3.14 430 */ 431 public bool isGroupedWith(Gesture other) 432 { 433 return gtk_gesture_is_grouped_with(gtkGesture, (other is null) ? null : other.getGestureStruct()) != 0; 434 } 435 436 /** 437 * Returns %TRUE if the gesture is currently recognized. 438 * A gesture is recognized if there are as many interacting 439 * touch sequences as required by @gesture, and #GtkGesture::check 440 * returned %TRUE for the sequences being currently interpreted. 441 * 442 * Return: %TRUE if gesture is recognized 443 * 444 * Since: 3.14 445 */ 446 public bool isRecognized() 447 { 448 return gtk_gesture_is_recognized(gtkGesture) != 0; 449 } 450 451 /** 452 * Sets the state of @sequence in @gesture. Sequences start 453 * in state #GTK_EVENT_SEQUENCE_NONE, and whenever they change 454 * state, they can never go back to that state. Likewise, 455 * sequences in state #GTK_EVENT_SEQUENCE_DENIED cannot turn 456 * back to a not denied state. With these rules, the lifetime 457 * of an event sequence is constrained to the next four: 458 * 459 * * None 460 * * None → Denied 461 * * None → Claimed 462 * * None → Claimed → Denied 463 * 464 * Note: Due to event handling ordering, it may be unsafe to 465 * set the state on another gesture within a #GtkGesture::begin 466 * signal handler, as the callback might be executed before 467 * the other gesture knows about the sequence. A safe way to 468 * perform this could be: 469 * 470 * |[ 471 * static void 472 * first_gesture_begin_cb (GtkGesture *first_gesture, 473 * GdkEventSequence *sequence, 474 * gpointer user_data) 475 * { 476 * gtk_gesture_set_sequence_state (first_gesture, sequence, GTK_EVENT_SEQUENCE_ACCEPTED); 477 * gtk_gesture_set_sequence_state (second_gesture, sequence, GTK_EVENT_SEQUENCE_DENIED); 478 * } 479 * 480 * static void 481 * second_gesture_begin_cb (GtkGesture *second_gesture, 482 * GdkEventSequence *sequence, 483 * gpointer user_data) 484 * { 485 * if (gtk_gesture_get_sequence_state (first_gesture, sequence) == GTK_EVENT_SEQUENCE_ACCEPTED) 486 * gtk_gesture_set_sequence_state (second_gesture, sequence, GTK_EVENT_SEQUENCE_DENIED); 487 * } 488 * ]| 489 * 490 * If both gestures are in the same group, just set the state on 491 * the gesture emitting the event, the sequence will be already 492 * be initialized to the group's global state when the second 493 * gesture processes the event. 494 * 495 * Params: 496 * sequence = a #GdkEventSequence 497 * state = the sequence state 498 * 499 * Return: %TRUE if @sequence is handled by @gesture, 500 * and the state is changed successfully 501 * 502 * Since: 3.14 503 */ 504 public bool setSequenceState(GdkEventSequence* sequence, GtkEventSequenceState state) 505 { 506 return gtk_gesture_set_sequence_state(gtkGesture, sequence, state) != 0; 507 } 508 509 /** 510 * Sets the state of all sequences that @gesture is currently 511 * interacting with. See gtk_gesture_set_sequence_state() 512 * for more details on sequence states. 513 * 514 * Params: 515 * state = the sequence state 516 * 517 * Return: %TRUE if the state of at least one sequence 518 * was changed successfully 519 * 520 * Since: 3.14 521 */ 522 public bool setState(GtkEventSequenceState state) 523 { 524 return gtk_gesture_set_state(gtkGesture, state) != 0; 525 } 526 527 /** 528 * Sets a specific window to receive events about, so @gesture 529 * will effectively handle only events targeting @window, or 530 * a child of it. @window must pertain to gtk_event_controller_get_widget(). 531 * 532 * Params: 533 * window = a #GdkWindow, or %NULL 534 * 535 * Since: 3.14 536 */ 537 public void setWindow(Window window) 538 { 539 gtk_gesture_set_window(gtkGesture, (window is null) ? null : window.getWindowStruct()); 540 } 541 542 /** 543 * Separates @gesture into an isolated group. 544 * 545 * Since: 3.14 546 */ 547 public void ungroup() 548 { 549 gtk_gesture_ungroup(gtkGesture); 550 } 551 552 protected class OnBeginDelegateWrapper 553 { 554 void delegate(GdkEventSequence*, Gesture) dlg; 555 gulong handlerId; 556 ConnectFlags flags; 557 this(void delegate(GdkEventSequence*, Gesture) dlg, gulong handlerId, ConnectFlags flags) 558 { 559 this.dlg = dlg; 560 this.handlerId = handlerId; 561 this.flags = flags; 562 } 563 } 564 protected OnBeginDelegateWrapper[] onBeginListeners; 565 566 /** 567 * This signal is emitted when the gesture is recognized. This means the 568 * number of touch sequences matches #GtkGesture:n-points, and the #GtkGesture::check 569 * handler(s) returned #TRUE. 570 * 571 * Note: These conditions may also happen when an extra touch (eg. a third touch 572 * on a 2-touches gesture) is lifted, in that situation @sequence won't pertain 573 * to the current set of active touches, so don't rely on this being true. 574 * 575 * Params: 576 * sequence = the #GdkEventSequence that made the gesture to be recognized 577 * 578 * Since: 3.14 579 */ 580 gulong addOnBegin(void delegate(GdkEventSequence*, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 581 { 582 onBeginListeners ~= new OnBeginDelegateWrapper(dlg, 0, connectFlags); 583 onBeginListeners[onBeginListeners.length - 1].handlerId = Signals.connectData( 584 this, 585 "begin", 586 cast(GCallback)&callBackBegin, 587 cast(void*)onBeginListeners[onBeginListeners.length - 1], 588 cast(GClosureNotify)&callBackBeginDestroy, 589 connectFlags); 590 return onBeginListeners[onBeginListeners.length - 1].handlerId; 591 } 592 593 extern(C) static void callBackBegin(GtkGesture* gestureStruct, GdkEventSequence* sequence,OnBeginDelegateWrapper wrapper) 594 { 595 wrapper.dlg(sequence, wrapper.outer); 596 } 597 598 extern(C) static void callBackBeginDestroy(OnBeginDelegateWrapper wrapper, GClosure* closure) 599 { 600 wrapper.outer.internalRemoveOnBegin(wrapper); 601 } 602 603 protected void internalRemoveOnBegin(OnBeginDelegateWrapper source) 604 { 605 foreach(index, wrapper; onBeginListeners) 606 { 607 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 608 { 609 onBeginListeners[index] = null; 610 onBeginListeners = std.algorithm.remove(onBeginListeners, index); 611 break; 612 } 613 } 614 } 615 616 617 protected class OnBeginGenericDelegateWrapper 618 { 619 void delegate(Event, Gesture) dlg; 620 gulong handlerId; 621 ConnectFlags flags; 622 this(void delegate(Event, Gesture) dlg, gulong handlerId, ConnectFlags flags) 623 { 624 this.dlg = dlg; 625 this.handlerId = handlerId; 626 this.flags = flags; 627 } 628 } 629 protected OnBeginGenericDelegateWrapper[] onBeginGenericListeners; 630 631 /** 632 * This signal is emitted when the gesture is recognized. This means the 633 * number of touch sequences matches #GtkGesture:n-points, and the #GtkGesture::check 634 * handler(s) returned #TRUE. 635 * 636 * Note: These conditions may also happen when an extra touch (eg. a third touch 637 * on a 2-touches gesture) is lifted, in that situation @sequence won't pertain 638 * to the current set of active touches, so don't rely on this being true. 639 * 640 * Params: 641 * sequence = the #GdkEventSequence that made the gesture to be recognized 642 * 643 * Since: 3.14 644 */ 645 gulong addOnBegin(void delegate(Event, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 646 { 647 onBeginGenericListeners ~= new OnBeginGenericDelegateWrapper(dlg, 0, connectFlags); 648 onBeginGenericListeners[onBeginGenericListeners.length - 1].handlerId = Signals.connectData( 649 this, 650 "begin", 651 cast(GCallback)&callBackBeginGeneric, 652 cast(void*)onBeginGenericListeners[onBeginGenericListeners.length - 1], 653 cast(GClosureNotify)&callBackBeginGenericDestroy, 654 connectFlags); 655 return onBeginGenericListeners[onBeginGenericListeners.length - 1].handlerId; 656 } 657 658 extern(C) static void callBackBeginGeneric(GtkGesture* gestureStruct, GdkEvent* sequence,OnBeginGenericDelegateWrapper wrapper) 659 { 660 wrapper.dlg(ObjectG.getDObject!(Event)(sequence), wrapper.outer); 661 } 662 663 extern(C) static void callBackBeginGenericDestroy(OnBeginGenericDelegateWrapper wrapper, GClosure* closure) 664 { 665 wrapper.outer.internalRemoveOnBeginGeneric(wrapper); 666 } 667 protected void internalRemoveOnBeginGeneric(OnBeginGenericDelegateWrapper source) 668 { 669 foreach(index, wrapper; onBeginGenericListeners) 670 { 671 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 672 { 673 onBeginGenericListeners[index] = null; 674 onBeginGenericListeners = std.algorithm.remove(onBeginGenericListeners, index); 675 break; 676 } 677 } 678 } 679 680 681 protected class OnCancelDelegateWrapper 682 { 683 void delegate(GdkEventSequence*, Gesture) dlg; 684 gulong handlerId; 685 ConnectFlags flags; 686 this(void delegate(GdkEventSequence*, Gesture) dlg, gulong handlerId, ConnectFlags flags) 687 { 688 this.dlg = dlg; 689 this.handlerId = handlerId; 690 this.flags = flags; 691 } 692 } 693 protected OnCancelDelegateWrapper[] onCancelListeners; 694 695 /** 696 * This signal is emitted whenever a sequence is cancelled. This usually 697 * happens on active touches when gtk_event_controller_reset() is called 698 * on @gesture (manually, due to grabs...), or the individual @sequence 699 * was claimed by parent widgets' controllers (see gtk_gesture_set_sequence_state()). 700 * 701 * @gesture must forget everything about @sequence as a reaction to this signal. 702 * 703 * Params: 704 * sequence = the #GdkEventSequence that was cancelled 705 * 706 * Since: 3.14 707 */ 708 gulong addOnCancel(void delegate(GdkEventSequence*, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 709 { 710 onCancelListeners ~= new OnCancelDelegateWrapper(dlg, 0, connectFlags); 711 onCancelListeners[onCancelListeners.length - 1].handlerId = Signals.connectData( 712 this, 713 "cancel", 714 cast(GCallback)&callBackCancel, 715 cast(void*)onCancelListeners[onCancelListeners.length - 1], 716 cast(GClosureNotify)&callBackCancelDestroy, 717 connectFlags); 718 return onCancelListeners[onCancelListeners.length - 1].handlerId; 719 } 720 721 extern(C) static void callBackCancel(GtkGesture* gestureStruct, GdkEventSequence* sequence,OnCancelDelegateWrapper wrapper) 722 { 723 wrapper.dlg(sequence, wrapper.outer); 724 } 725 726 extern(C) static void callBackCancelDestroy(OnCancelDelegateWrapper wrapper, GClosure* closure) 727 { 728 wrapper.outer.internalRemoveOnCancel(wrapper); 729 } 730 731 protected void internalRemoveOnCancel(OnCancelDelegateWrapper source) 732 { 733 foreach(index, wrapper; onCancelListeners) 734 { 735 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 736 { 737 onCancelListeners[index] = null; 738 onCancelListeners = std.algorithm.remove(onCancelListeners, index); 739 break; 740 } 741 } 742 } 743 744 745 protected class OnCancelGenericDelegateWrapper 746 { 747 void delegate(Event, Gesture) dlg; 748 gulong handlerId; 749 ConnectFlags flags; 750 this(void delegate(Event, Gesture) dlg, gulong handlerId, ConnectFlags flags) 751 { 752 this.dlg = dlg; 753 this.handlerId = handlerId; 754 this.flags = flags; 755 } 756 } 757 protected OnCancelGenericDelegateWrapper[] onCancelGenericListeners; 758 759 /** 760 * This signal is emitted whenever a sequence is cancelled. This usually 761 * happens on active touches when gtk_event_controller_reset() is called 762 * on @gesture (manually, due to grabs...), or the individual @sequence 763 * was claimed by parent widgets' controllers (see gtk_gesture_set_sequence_state()). 764 * 765 * @gesture must forget everything about @sequence as a reaction to this signal. 766 * 767 * Params: 768 * sequence = the #GdkEventSequence that was cancelled 769 * 770 * Since: 3.14 771 */ 772 gulong addOnCancel(void delegate(Event, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 773 { 774 onCancelGenericListeners ~= new OnCancelGenericDelegateWrapper(dlg, 0, connectFlags); 775 onCancelGenericListeners[onCancelGenericListeners.length - 1].handlerId = Signals.connectData( 776 this, 777 "cancel", 778 cast(GCallback)&callBackCancelGeneric, 779 cast(void*)onCancelGenericListeners[onCancelGenericListeners.length - 1], 780 cast(GClosureNotify)&callBackCancelGenericDestroy, 781 connectFlags); 782 return onCancelGenericListeners[onCancelGenericListeners.length - 1].handlerId; 783 } 784 785 extern(C) static void callBackCancelGeneric(GtkGesture* gestureStruct, GdkEvent* sequence,OnCancelGenericDelegateWrapper wrapper) 786 { 787 wrapper.dlg(ObjectG.getDObject!(Event)(sequence), wrapper.outer); 788 } 789 790 extern(C) static void callBackCancelGenericDestroy(OnCancelGenericDelegateWrapper wrapper, GClosure* closure) 791 { 792 wrapper.outer.internalRemoveOnCancelGeneric(wrapper); 793 } 794 protected void internalRemoveOnCancelGeneric(OnCancelGenericDelegateWrapper source) 795 { 796 foreach(index, wrapper; onCancelGenericListeners) 797 { 798 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 799 { 800 onCancelGenericListeners[index] = null; 801 onCancelGenericListeners = std.algorithm.remove(onCancelGenericListeners, index); 802 break; 803 } 804 } 805 } 806 807 808 protected class OnEndDelegateWrapper 809 { 810 void delegate(GdkEventSequence*, Gesture) dlg; 811 gulong handlerId; 812 ConnectFlags flags; 813 this(void delegate(GdkEventSequence*, Gesture) dlg, gulong handlerId, ConnectFlags flags) 814 { 815 this.dlg = dlg; 816 this.handlerId = handlerId; 817 this.flags = flags; 818 } 819 } 820 protected OnEndDelegateWrapper[] onEndListeners; 821 822 /** 823 * This signal is emitted when @gesture either stopped recognizing the event 824 * sequences as something to be handled (the #GtkGesture::check handler returned 825 * %FALSE), or the number of touch sequences became higher or lower than 826 * #GtkGesture:n-points. 827 * 828 * Note: @sequence might not pertain to the group of sequences that were 829 * previously triggering recognition on @gesture (ie. a just pressed touch 830 * sequence that exceeds #GtkGesture:n-points). This situation may be detected 831 * by checking through gtk_gesture_handles_sequence(). 832 * 833 * Params: 834 * sequence = the #GdkEventSequence that made gesture recognition to finish 835 * 836 * Since: 3.14 837 */ 838 gulong addOnEnd(void delegate(GdkEventSequence*, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 839 { 840 onEndListeners ~= new OnEndDelegateWrapper(dlg, 0, connectFlags); 841 onEndListeners[onEndListeners.length - 1].handlerId = Signals.connectData( 842 this, 843 "end", 844 cast(GCallback)&callBackEnd, 845 cast(void*)onEndListeners[onEndListeners.length - 1], 846 cast(GClosureNotify)&callBackEndDestroy, 847 connectFlags); 848 return onEndListeners[onEndListeners.length - 1].handlerId; 849 } 850 851 extern(C) static void callBackEnd(GtkGesture* gestureStruct, GdkEventSequence* sequence,OnEndDelegateWrapper wrapper) 852 { 853 wrapper.dlg(sequence, wrapper.outer); 854 } 855 856 extern(C) static void callBackEndDestroy(OnEndDelegateWrapper wrapper, GClosure* closure) 857 { 858 wrapper.outer.internalRemoveOnEnd(wrapper); 859 } 860 861 protected void internalRemoveOnEnd(OnEndDelegateWrapper source) 862 { 863 foreach(index, wrapper; onEndListeners) 864 { 865 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 866 { 867 onEndListeners[index] = null; 868 onEndListeners = std.algorithm.remove(onEndListeners, index); 869 break; 870 } 871 } 872 } 873 874 875 protected class OnEndGenericDelegateWrapper 876 { 877 void delegate(Event, Gesture) dlg; 878 gulong handlerId; 879 ConnectFlags flags; 880 this(void delegate(Event, Gesture) dlg, gulong handlerId, ConnectFlags flags) 881 { 882 this.dlg = dlg; 883 this.handlerId = handlerId; 884 this.flags = flags; 885 } 886 } 887 protected OnEndGenericDelegateWrapper[] onEndGenericListeners; 888 889 /** 890 * This signal is emitted when @gesture either stopped recognizing the event 891 * sequences as something to be handled (the #GtkGesture::check handler returned 892 * %FALSE), or the number of touch sequences became higher or lower than 893 * #GtkGesture:n-points. 894 * 895 * Note: @sequence might not pertain to the group of sequences that were 896 * previously triggering recognition on @gesture (ie. a just pressed touch 897 * sequence that exceeds #GtkGesture:n-points). This situation may be detected 898 * by checking through gtk_gesture_handles_sequence(). 899 * 900 * Params: 901 * sequence = the #GdkEventSequence that made gesture recognition to finish 902 * 903 * Since: 3.14 904 */ 905 gulong addOnEnd(void delegate(Event, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 906 { 907 onEndGenericListeners ~= new OnEndGenericDelegateWrapper(dlg, 0, connectFlags); 908 onEndGenericListeners[onEndGenericListeners.length - 1].handlerId = Signals.connectData( 909 this, 910 "end", 911 cast(GCallback)&callBackEndGeneric, 912 cast(void*)onEndGenericListeners[onEndGenericListeners.length - 1], 913 cast(GClosureNotify)&callBackEndGenericDestroy, 914 connectFlags); 915 return onEndGenericListeners[onEndGenericListeners.length - 1].handlerId; 916 } 917 918 extern(C) static void callBackEndGeneric(GtkGesture* gestureStruct, GdkEvent* sequence,OnEndGenericDelegateWrapper wrapper) 919 { 920 wrapper.dlg(ObjectG.getDObject!(Event)(sequence), wrapper.outer); 921 } 922 923 extern(C) static void callBackEndGenericDestroy(OnEndGenericDelegateWrapper wrapper, GClosure* closure) 924 { 925 wrapper.outer.internalRemoveOnEndGeneric(wrapper); 926 } 927 protected void internalRemoveOnEndGeneric(OnEndGenericDelegateWrapper source) 928 { 929 foreach(index, wrapper; onEndGenericListeners) 930 { 931 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 932 { 933 onEndGenericListeners[index] = null; 934 onEndGenericListeners = std.algorithm.remove(onEndGenericListeners, index); 935 break; 936 } 937 } 938 } 939 940 941 protected class OnSequenceStateChangedDelegateWrapper 942 { 943 void delegate(GdkEventSequence*, GtkEventSequenceState, Gesture) dlg; 944 gulong handlerId; 945 ConnectFlags flags; 946 this(void delegate(GdkEventSequence*, GtkEventSequenceState, Gesture) dlg, gulong handlerId, ConnectFlags flags) 947 { 948 this.dlg = dlg; 949 this.handlerId = handlerId; 950 this.flags = flags; 951 } 952 } 953 protected OnSequenceStateChangedDelegateWrapper[] onSequenceStateChangedListeners; 954 955 /** 956 * This signal is emitted whenever a sequence state changes. See 957 * gtk_gesture_set_sequence_state() to know more about the expectable 958 * sequence lifetimes. 959 * 960 * Params: 961 * sequence = the #GdkEventSequence that was cancelled 962 * state = the new sequence state 963 * 964 * Since: 3.14 965 */ 966 gulong addOnSequenceStateChanged(void delegate(GdkEventSequence*, GtkEventSequenceState, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 967 { 968 onSequenceStateChangedListeners ~= new OnSequenceStateChangedDelegateWrapper(dlg, 0, connectFlags); 969 onSequenceStateChangedListeners[onSequenceStateChangedListeners.length - 1].handlerId = Signals.connectData( 970 this, 971 "sequence-state-changed", 972 cast(GCallback)&callBackSequenceStateChanged, 973 cast(void*)onSequenceStateChangedListeners[onSequenceStateChangedListeners.length - 1], 974 cast(GClosureNotify)&callBackSequenceStateChangedDestroy, 975 connectFlags); 976 return onSequenceStateChangedListeners[onSequenceStateChangedListeners.length - 1].handlerId; 977 } 978 979 extern(C) static void callBackSequenceStateChanged(GtkGesture* gestureStruct, GdkEventSequence* sequence, GtkEventSequenceState state,OnSequenceStateChangedDelegateWrapper wrapper) 980 { 981 wrapper.dlg(sequence, state, wrapper.outer); 982 } 983 984 extern(C) static void callBackSequenceStateChangedDestroy(OnSequenceStateChangedDelegateWrapper wrapper, GClosure* closure) 985 { 986 wrapper.outer.internalRemoveOnSequenceStateChanged(wrapper); 987 } 988 989 protected void internalRemoveOnSequenceStateChanged(OnSequenceStateChangedDelegateWrapper source) 990 { 991 foreach(index, wrapper; onSequenceStateChangedListeners) 992 { 993 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 994 { 995 onSequenceStateChangedListeners[index] = null; 996 onSequenceStateChangedListeners = std.algorithm.remove(onSequenceStateChangedListeners, index); 997 break; 998 } 999 } 1000 } 1001 1002 1003 protected class OnSequenceStateChangedGenericDelegateWrapper 1004 { 1005 void delegate(Event, GtkEventSequenceState, Gesture) dlg; 1006 gulong handlerId; 1007 ConnectFlags flags; 1008 this(void delegate(Event, GtkEventSequenceState, Gesture) dlg, gulong handlerId, ConnectFlags flags) 1009 { 1010 this.dlg = dlg; 1011 this.handlerId = handlerId; 1012 this.flags = flags; 1013 } 1014 } 1015 protected OnSequenceStateChangedGenericDelegateWrapper[] onSequenceStateChangedGenericListeners; 1016 1017 /** 1018 * This signal is emitted whenever a sequence state changes. See 1019 * gtk_gesture_set_sequence_state() to know more about the expectable 1020 * sequence lifetimes. 1021 * 1022 * Params: 1023 * sequence = the #GdkEventSequence that was cancelled 1024 * state = the new sequence state 1025 * 1026 * Since: 3.14 1027 */ 1028 gulong addOnSequenceStateChanged(void delegate(Event, GtkEventSequenceState, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 1029 { 1030 onSequenceStateChangedGenericListeners ~= new OnSequenceStateChangedGenericDelegateWrapper(dlg, 0, connectFlags); 1031 onSequenceStateChangedGenericListeners[onSequenceStateChangedGenericListeners.length - 1].handlerId = Signals.connectData( 1032 this, 1033 "sequence-state-changed", 1034 cast(GCallback)&callBackSequenceStateChangedGeneric, 1035 cast(void*)onSequenceStateChangedGenericListeners[onSequenceStateChangedGenericListeners.length - 1], 1036 cast(GClosureNotify)&callBackSequenceStateChangedGenericDestroy, 1037 connectFlags); 1038 return onSequenceStateChangedGenericListeners[onSequenceStateChangedGenericListeners.length - 1].handlerId; 1039 } 1040 1041 extern(C) static void callBackSequenceStateChangedGeneric(GtkGesture* gestureStruct, GdkEvent* sequence, GtkEventSequenceState state,OnSequenceStateChangedGenericDelegateWrapper wrapper) 1042 { 1043 wrapper.dlg(ObjectG.getDObject!(Event)(sequence), state, wrapper.outer); 1044 } 1045 1046 extern(C) static void callBackSequenceStateChangedGenericDestroy(OnSequenceStateChangedGenericDelegateWrapper wrapper, GClosure* closure) 1047 { 1048 wrapper.outer.internalRemoveOnSequenceStateChangedGeneric(wrapper); 1049 } 1050 protected void internalRemoveOnSequenceStateChangedGeneric(OnSequenceStateChangedGenericDelegateWrapper source) 1051 { 1052 foreach(index, wrapper; onSequenceStateChangedGenericListeners) 1053 { 1054 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 1055 { 1056 onSequenceStateChangedGenericListeners[index] = null; 1057 onSequenceStateChangedGenericListeners = std.algorithm.remove(onSequenceStateChangedGenericListeners, index); 1058 break; 1059 } 1060 } 1061 } 1062 1063 1064 protected class OnUpdateDelegateWrapper 1065 { 1066 void delegate(GdkEventSequence*, Gesture) dlg; 1067 gulong handlerId; 1068 ConnectFlags flags; 1069 this(void delegate(GdkEventSequence*, Gesture) dlg, gulong handlerId, ConnectFlags flags) 1070 { 1071 this.dlg = dlg; 1072 this.handlerId = handlerId; 1073 this.flags = flags; 1074 } 1075 } 1076 protected OnUpdateDelegateWrapper[] onUpdateListeners; 1077 1078 /** 1079 * This signal is emitted whenever an event is handled while the gesture is 1080 * recognized. @sequence is guaranteed to pertain to the set of active touches. 1081 * 1082 * Params: 1083 * sequence = the #GdkEventSequence that was updated 1084 * 1085 * Since: 3.14 1086 */ 1087 gulong addOnUpdate(void delegate(GdkEventSequence*, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 1088 { 1089 onUpdateListeners ~= new OnUpdateDelegateWrapper(dlg, 0, connectFlags); 1090 onUpdateListeners[onUpdateListeners.length - 1].handlerId = Signals.connectData( 1091 this, 1092 "update", 1093 cast(GCallback)&callBackUpdate, 1094 cast(void*)onUpdateListeners[onUpdateListeners.length - 1], 1095 cast(GClosureNotify)&callBackUpdateDestroy, 1096 connectFlags); 1097 return onUpdateListeners[onUpdateListeners.length - 1].handlerId; 1098 } 1099 1100 extern(C) static void callBackUpdate(GtkGesture* gestureStruct, GdkEventSequence* sequence,OnUpdateDelegateWrapper wrapper) 1101 { 1102 wrapper.dlg(sequence, wrapper.outer); 1103 } 1104 1105 extern(C) static void callBackUpdateDestroy(OnUpdateDelegateWrapper wrapper, GClosure* closure) 1106 { 1107 wrapper.outer.internalRemoveOnUpdate(wrapper); 1108 } 1109 1110 protected void internalRemoveOnUpdate(OnUpdateDelegateWrapper source) 1111 { 1112 foreach(index, wrapper; onUpdateListeners) 1113 { 1114 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 1115 { 1116 onUpdateListeners[index] = null; 1117 onUpdateListeners = std.algorithm.remove(onUpdateListeners, index); 1118 break; 1119 } 1120 } 1121 } 1122 1123 1124 protected class OnUpdateGenericDelegateWrapper 1125 { 1126 void delegate(Event, Gesture) dlg; 1127 gulong handlerId; 1128 ConnectFlags flags; 1129 this(void delegate(Event, Gesture) dlg, gulong handlerId, ConnectFlags flags) 1130 { 1131 this.dlg = dlg; 1132 this.handlerId = handlerId; 1133 this.flags = flags; 1134 } 1135 } 1136 protected OnUpdateGenericDelegateWrapper[] onUpdateGenericListeners; 1137 1138 /** 1139 * This signal is emitted whenever an event is handled while the gesture is 1140 * recognized. @sequence is guaranteed to pertain to the set of active touches. 1141 * 1142 * Params: 1143 * sequence = the #GdkEventSequence that was updated 1144 * 1145 * Since: 3.14 1146 */ 1147 gulong addOnUpdate(void delegate(Event, Gesture) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 1148 { 1149 onUpdateGenericListeners ~= new OnUpdateGenericDelegateWrapper(dlg, 0, connectFlags); 1150 onUpdateGenericListeners[onUpdateGenericListeners.length - 1].handlerId = Signals.connectData( 1151 this, 1152 "update", 1153 cast(GCallback)&callBackUpdateGeneric, 1154 cast(void*)onUpdateGenericListeners[onUpdateGenericListeners.length - 1], 1155 cast(GClosureNotify)&callBackUpdateGenericDestroy, 1156 connectFlags); 1157 return onUpdateGenericListeners[onUpdateGenericListeners.length - 1].handlerId; 1158 } 1159 1160 extern(C) static void callBackUpdateGeneric(GtkGesture* gestureStruct, GdkEvent* sequence,OnUpdateGenericDelegateWrapper wrapper) 1161 { 1162 wrapper.dlg(ObjectG.getDObject!(Event)(sequence), wrapper.outer); 1163 } 1164 1165 extern(C) static void callBackUpdateGenericDestroy(OnUpdateGenericDelegateWrapper wrapper, GClosure* closure) 1166 { 1167 wrapper.outer.internalRemoveOnUpdateGeneric(wrapper); 1168 } 1169 protected void internalRemoveOnUpdateGeneric(OnUpdateGenericDelegateWrapper source) 1170 { 1171 foreach(index, wrapper; onUpdateGenericListeners) 1172 { 1173 if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId) 1174 { 1175 onUpdateGenericListeners[index] = null; 1176 onUpdateGenericListeners = std.algorithm.remove(onUpdateGenericListeners, index); 1177 break; 1178 } 1179 } 1180 } 1181 1182 }