From d198428dc486725e0ad94d475b77c9d7e805f8a4 Mon Sep 17 00:00:00 2001 From: Si Date: Wed, 23 Sep 2026 22:26:59 +0200 Subject: [PATCH] Fit the split on a double-click only once the handle's drag ends The split's own drag is still going during the second press of a double-click, so the least movement before the release put the handle back under the pointer right after it was fitted. The fit now waits for that drag to end. Co-Authored-By: Claude Opus 5.5 --- src/gui/window.cpp | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 278083e..47f9bd6 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -227,10 +227,30 @@ Gtk::Widget& EffectorWindow::buildChainTab() { auto click = Gtk::GestureClick::create(); click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); // The split's drag ends every press on the handle, which starts the - // click's count over, so the second press is counted here. + // click's count over, so the second press is counted here. And that + // drag is still going on during the second press -- the least jitter + // puts the handle back under the pointer -- so the fit waits for it + // to end. + std::vector> drags; + const auto controllers = split->observe_controllers(); + for (guint i = 0; i < controllers->get_n_items(); ++i) { + auto other = std::dynamic_pointer_cast(controllers->get_object(i)); + if (other) click->group(other); + if (auto drag = std::dynamic_pointer_cast(other)) drags.push_back(drag); + } auto lastPress = std::make_shared(0); + auto refitPending = std::make_shared(false); + for (const auto& drag : drags) { + drag->signal_drag_end().connect([this, refitPending](double, double) { + if (!*refitPending) return; + *refitPending = false; + refitChainSplit(); + }); + } auto* gesture = click.get(); // not the RefPtr: the gesture owns this slot - click->signal_pressed().connect([this, split, handle, gesture, lastPress](int, double x, double y) { + const bool waitForDrag = !drags.empty(); + click->signal_pressed().connect( + [this, split, handle, gesture, lastPress, refitPending, waitForDrag](int, double x, double y) { // The handle is thin, and the drag takes presses a little past it. const auto bounds = handle->compute_bounds(*split); constexpr double slack = 6.0; @@ -244,16 +264,15 @@ Gtk::Widget& EffectorWindow::buildChainTab() { const int doubleClick = Gtk::Settings::get_default()->property_gtk_double_click_time(); if (*lastPress != 0 && now - *lastPress <= static_cast(doubleClick)) { *lastPress = 0; - refitChainSplit(); + if (waitForDrag) { + *refitPending = true; + } else { + refitChainSplit(); + } } else { *lastPress = now; } }); - const auto controllers = split->observe_controllers(); - for (guint i = 0; i < controllers->get_n_items(); ++i) { - auto drag = std::dynamic_pointer_cast(controllers->get_object(i)); - if (drag) click->group(drag); - } split->add_controller(click); } rebuildChainRows();