|
21 | 21 | #include <gtkmm/menu.h> |
22 | 22 | #include <gtkmm/menubar.h> |
23 | 23 | #include <gtkmm/messagedialog.h> |
| 24 | +#include <gtkmm/settings.h> |
24 | 25 | #include <gtkmm/scrollbar.h> |
25 | 26 | #include <gtkmm/separatormenuitem.h> |
26 | 27 | #include <gtkmm/tooltip.h> |
@@ -456,10 +457,18 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) { |
456 | 457 | //----------------------------------------------------------------------------- |
457 | 458 |
|
458 | 459 | class GtkGLWidget : public Gtk::GLArea { |
| 460 | + struct LastPress { |
| 461 | + double x, y; |
| 462 | + guint button; |
| 463 | + guint32 time; |
| 464 | + GdkDevice *device; |
| 465 | + }; |
| 466 | + |
459 | 467 | Window *_receiver; |
| 468 | + LastPress _last_press; |
460 | 469 |
|
461 | 470 | public: |
462 | | - GtkGLWidget(Platform::Window *receiver) : _receiver(receiver) { |
| 471 | + GtkGLWidget(Platform::Window *receiver) : _receiver(receiver), _last_press() { |
463 | 472 | set_has_depth_buffer(true); |
464 | 473 | set_can_focus(true); |
465 | 474 | set_events(Gdk::POINTER_MOTION_MASK | |
@@ -549,6 +558,59 @@ class GtkGLWidget : public Gtk::GLArea { |
549 | 558 | guint button; |
550 | 559 | gdk_event_get_button((GdkEvent*)gdk_event, &button); |
551 | 560 |
|
| 561 | + // In GTK, the sequence of events for a double click is: |
| 562 | + // - press |
| 563 | + // - release |
| 564 | + // - press |
| 565 | + // - double-press |
| 566 | + // - release |
| 567 | + // |
| 568 | + // Having a press event right before the double press event is inconsistent with |
| 569 | + // the way double click handling works on Windows and macOS, and may cause receiver |
| 570 | + // code to treat the sequence as three press events. |
| 571 | + // To avoid this, we check if a press event happened quickly enough after the |
| 572 | + // previous one to be considered a double press, and eat it up if a receiver |
| 573 | + // is set up. |
| 574 | + // Since we ignore triple press events, we reset the last press data when receiving |
| 575 | + // the double press event, in order to avoid eating up the third press event of |
| 576 | + // a triple click sequence. |
| 577 | + if(gdk_type == GDK_BUTTON_PRESS) { |
| 578 | + LastPress c = { |
| 579 | + x, y, button, |
| 580 | + gdk_event_get_time((GdkEvent*)gdk_event), |
| 581 | + gdk_event_get_device((GdkEvent*)gdk_event), |
| 582 | + }; |
| 583 | + LastPress p = _last_press; |
| 584 | + _last_press = c; |
| 585 | + |
| 586 | + if(_receiver->onMouseEvent) { |
| 587 | + const Glib::RefPtr<Gtk::Settings> settings = get_settings(); |
| 588 | + const guint dbl_press_time = settings-> |
| 589 | + property_gtk_double_click_time().get_value(); |
| 590 | + const double dbl_press_distance = settings-> |
| 591 | + property_gtk_double_click_distance().get_value(); |
| 592 | + |
| 593 | + if(c.device == p.device && |
| 594 | + c.button == p.button && |
| 595 | + c.time - p.time < dbl_press_time && |
| 596 | + std::abs(c.x - p.x) <= dbl_press_distance && |
| 597 | + std::abs(c.y - p.y) <= dbl_press_distance) { |
| 598 | + // Eat this press event, as it'll be followed immediately |
| 599 | + // by a double press event |
| 600 | + return true; |
| 601 | + } |
| 602 | + } |
| 603 | + } else if(gdk_type == GDK_2BUTTON_PRESS) { |
| 604 | + const guint32 time = gdk_event_get_time((GdkEvent*)gdk_event); |
| 605 | + const GdkDevice *device = gdk_event_get_device((GdkEvent*)gdk_event); |
| 606 | + // A double press event is synthesised by GTK, and has the exact same |
| 607 | + // time and device as the press event the preceeded it. |
| 608 | + ssassert(_last_press.time == time && _last_press.device == device, |
| 609 | + "double press event not following a press event"); |
| 610 | + // Reset in order to avoid eating the press event for a triple click |
| 611 | + _last_press = {}; |
| 612 | + } |
| 613 | + |
552 | 614 | if(process_pointer_event(type, x, y, state, button)) |
553 | 615 | return true; |
554 | 616 |
|
|
0 commit comments