Skip to content

Commit 925a527

Browse files
iscgarphkahler
authored andcommitted
guigtk: make double click handling consistent with Windows and macOS
GTK sends a press event for the 2nd click before synthesising a double click event. This is inconsistent with the GUI code on Windows and macOS and causes double clicks to be interpreted as triple clicks by various receivers. Fix it by saving the last press event, and eating the current event when it would be considered a double click by GTK and a receiver is set up, since a double click event will follow immediately.
1 parent 2e0c947 commit 925a527

1 file changed

Lines changed: 63 additions & 1 deletion

File tree

src/platform/guigtk.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <gtkmm/menu.h>
2222
#include <gtkmm/menubar.h>
2323
#include <gtkmm/messagedialog.h>
24+
#include <gtkmm/settings.h>
2425
#include <gtkmm/scrollbar.h>
2526
#include <gtkmm/separatormenuitem.h>
2627
#include <gtkmm/tooltip.h>
@@ -456,10 +457,18 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
456457
//-----------------------------------------------------------------------------
457458

458459
class GtkGLWidget : public Gtk::GLArea {
460+
struct LastPress {
461+
double x, y;
462+
guint button;
463+
guint32 time;
464+
GdkDevice *device;
465+
};
466+
459467
Window *_receiver;
468+
LastPress _last_press;
460469

461470
public:
462-
GtkGLWidget(Platform::Window *receiver) : _receiver(receiver) {
471+
GtkGLWidget(Platform::Window *receiver) : _receiver(receiver), _last_press() {
463472
set_has_depth_buffer(true);
464473
set_can_focus(true);
465474
set_events(Gdk::POINTER_MOTION_MASK |
@@ -549,6 +558,59 @@ class GtkGLWidget : public Gtk::GLArea {
549558
guint button;
550559
gdk_event_get_button((GdkEvent*)gdk_event, &button);
551560

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+
552614
if(process_pointer_event(type, x, y, state, button))
553615
return true;
554616

0 commit comments

Comments
 (0)