diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 2bb0ba8dbe02e0c6bf4efea1e516df0994152794..93badabfe7dcea7b47e5b8ed1deb6c5643a832ea 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -338,7 +338,6 @@ bool ToolBase::root_handler(CanvasEvent const &event) inspect_event(event, [&] (ButtonPressEvent const &event) { - if (event.num_press == 2) { if (panning) { panning = PANNING_NONE; @@ -352,6 +351,8 @@ bool ToolBase::root_handler(CanvasEvent const &event) button_w = event.pos; + check_lost_button_up = !(event.device && event.device->get_source() != Gdk::InputSource::TABLET_PAD); + switch (event.button) { case 1: // TODO Does this make sense? Panning starts on passive mouse motion while space @@ -432,10 +433,9 @@ bool ToolBase::root_handler(CanvasEvent const &event) EventType::MOTION); } - if ((panning == 2 && !(event.modifiers & GDK_BUTTON2_MASK)) || - (panning == 1 && !(event.modifiers & GDK_BUTTON1_MASK)) || - (panning == 3 && !(event.modifiers & GDK_BUTTON3_MASK))) - { + if (check_lost_button_up && ((panning == 2 && !(event.modifiers & GDK_BUTTON2_MASK)) || + (panning == 1 && !(event.modifiers & GDK_BUTTON1_MASK)) || + (panning == 3 && !(event.modifiers & GDK_BUTTON3_MASK)))) { // Gdk seems to lose button release for us sometimes :-( panning = PANNING_NONE; ungrabCanvasEvents(); @@ -1151,10 +1151,15 @@ bool ToolBase::tool_root_handler(CanvasEvent const &event) // Panning has priority over tool-specific event handling if (is_panning()) { - return ToolBase::root_handler(event); - } else { - return root_handler(event); + if (ToolBase::root_handler(event)) { + return true; + } + if (event.type() != EventType::BUTTON_RELEASE) { + return false; + } + // still try to deliver button release events not consumed by panning } + return root_handler(event); } /** diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h index ef607c78ec643057ddcef2db99e871ea59a8ad1c..e66df77882b3a8c5c9d7bc4de834b681cd08fd59 100644 --- a/src/ui/tools/tool-base.h +++ b/src/ui/tools/tool-base.h @@ -165,6 +165,7 @@ private: bool rotating = false; double start_angle, current_angle; + bool check_lost_button_up = false; public: bool start_root_handler(CanvasEvent const &event); diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index 6620c75dfbe3a7400d90f6b9b4e38464019c9945..99e3a9fd139bcbc7db4608204e4c4b31b5bc3a76 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -316,11 +316,31 @@ Canvas::Canvas() scroll->signal_scroll().connect([this, &scroll = *scroll](auto &&...args) { return on_scroll(scroll, args...); }, true); add_controller(scroll); - auto const click = Gtk::GestureClick::create(); - click->set_button(0); - click->signal_pressed().connect(Controller::use_state([this](auto &&...args) { return on_button_pressed(args...); }, *click)); - click->signal_released().connect(Controller::use_state([this](auto &&...args) { return on_button_released(args...); }, *click)); - add_controller(click); + unsigned const MAX_BUTTON = 5; + for (unsigned int i = 1; i <= MAX_BUTTON; i++) { + auto const click = Gtk::GestureClick::create(); + // Intentionally use separate gestures for each button instead of common one with set_button(0) + // For sequence Left_down, Middle_down, Middle_up, Left_down the common mode would produce: + // pressed(left), cancelled, unpaired_release(middle), unpaired_release(left) + // The middle click interrupts gesture but doesn't produce a button down event. + // With separate gestures second click still interrupts first one but the pressed signal doesn't get lost. + click->set_button(i); + click->signal_pressed().connect( + Controller::use_state([this](auto &&...args) { return on_button_pressed(args...); }, *click)); + click->signal_released().connect(Controller::use_state( + [this](Gtk::GestureClick &gesture, auto &&...args) { + return on_button_released(gesture, args..., gesture.get_current_button()); + }, + *click)); + // smallest deviation from normal flow will interrupt gesture and classify release as unpaired + click->signal_unpaired_release().connect( + [this, i, &controller = *click](double x, double y, guint button, Gdk::EventSequence *) { + if (i == button) { + on_button_released(controller, 0, x, y, button); + } + }); + add_controller(click); + } auto const key = Gtk::EventControllerKey::create(); key->signal_key_pressed().connect([this, &key = *key](auto &&...args) { return on_key_pressed(key, args...); }, true); @@ -995,12 +1015,12 @@ Gtk::EventSequenceState Canvas::on_button_pressed(Gtk::GestureClick const &contr return result ? Gtk::EventSequenceState::CLAIMED : Gtk::EventSequenceState::NONE; } -Gtk::EventSequenceState Canvas::on_button_released(Gtk::GestureClick const &controller, - int /*n_press*/, double x, double y) +Gtk::EventSequenceState Canvas::on_button_released(Gtk::GestureClick const &controller, int /*n_press*/, double x, + double y, int button) { _state = (int)controller.get_current_event_state(); d->last_mouse = Geom::Point(x, y); - d->unreleased_presses &= ~(1 << controller.get_current_button()); + d->unreleased_presses &= ~(1 << button); // Drag the split view controller. if (_split_mode == SplitMode::SPLIT && _split_dragging) { @@ -1039,7 +1059,6 @@ Gtk::EventSequenceState Canvas::on_button_released(Gtk::GestureClick const &cont } } - auto const button = controller.get_current_button(); if (button == 1) { d->autoscroll_end(); } @@ -1048,7 +1067,7 @@ Gtk::EventSequenceState Canvas::on_button_released(Gtk::GestureClick const &cont event.modifiers = _state; event.device = controller.get_current_event_device(); event.orig_pos = *d->last_mouse; - event.button = controller.get_current_button(); + event.button = button; event.time = controller.get_current_event_time(); auto result = d->process_event(event) ? Gtk::EventSequenceState::CLAIMED : Gtk::EventSequenceState::NONE; diff --git a/src/ui/widget/canvas.h b/src/ui/widget/canvas.h index 1aa3f1a1c592a64eda8b03fc2bcfc349705a2e88..082b842f43cfefb35d39c004ca7df746bc584d41 100644 --- a/src/ui/widget/canvas.h +++ b/src/ui/widget/canvas.h @@ -154,8 +154,8 @@ private: // GestureClick Gtk::EventSequenceState on_button_pressed (Gtk::GestureClick const &controller, int n_press, double x, double y); - Gtk::EventSequenceState on_button_released(Gtk::GestureClick const &controller, - int n_press, double x, double y); + Gtk::EventSequenceState on_button_released(Gtk::GestureClick const &controller, int n_press, double x, double y, + int button); // EventControllerMotion void on_motion(Gtk::EventControllerMotion const &controller, double x, double y);