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 <noreply@anthropic.com>
This commit is contained in:
Si
2026-09-23 22:26:59 +02:00
co-authored by Claude Opus 5.5
parent a01ada5372
commit d198428dc4
+27 -8
View File
@@ -227,10 +227,30 @@ Gtk::Widget& EffectorWindow::buildChainTab() {
auto click = Gtk::GestureClick::create(); auto click = Gtk::GestureClick::create();
click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); click->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
// The split's drag ends every press on the handle, which starts the // 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<Glib::RefPtr<Gtk::GestureDrag>> drags;
const auto controllers = split->observe_controllers();
for (guint i = 0; i < controllers->get_n_items(); ++i) {
auto other = std::dynamic_pointer_cast<Gtk::Gesture>(controllers->get_object(i));
if (other) click->group(other);
if (auto drag = std::dynamic_pointer_cast<Gtk::GestureDrag>(other)) drags.push_back(drag);
}
auto lastPress = std::make_shared<guint32>(0); auto lastPress = std::make_shared<guint32>(0);
auto refitPending = std::make_shared<bool>(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 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. // The handle is thin, and the drag takes presses a little past it.
const auto bounds = handle->compute_bounds(*split); const auto bounds = handle->compute_bounds(*split);
constexpr double slack = 6.0; 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(); const int doubleClick = Gtk::Settings::get_default()->property_gtk_double_click_time();
if (*lastPress != 0 && now - *lastPress <= static_cast<guint32>(doubleClick)) { if (*lastPress != 0 && now - *lastPress <= static_cast<guint32>(doubleClick)) {
*lastPress = 0; *lastPress = 0;
refitChainSplit(); if (waitForDrag) {
*refitPending = true;
} else {
refitChainSplit();
}
} else { } else {
*lastPress = now; *lastPress = now;
} }
}); });
const auto controllers = split->observe_controllers();
for (guint i = 0; i < controllers->get_n_items(); ++i) {
auto drag = std::dynamic_pointer_cast<Gtk::Gesture>(controllers->get_object(i));
if (drag) click->group(drag);
}
split->add_controller(click); split->add_controller(click);
} }
rebuildChainRows(); rebuildChainRows();