From 7cd35c866a52857a40054e39eba2e16f5776d138 Mon Sep 17 00:00:00 2001 From: Fernando Date: Sun, 17 May 2026 17:58:45 -0400 Subject: [PATCH] review(morph): morph Flickable non-interactive, proxy wheel scroll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex follow-up on PR #582 (merged): The new Flickable in the morph band defaulted to `interactive: true`, which consumed left-button drags in that region. `timelineArea` (the root marquee-selection MouseArea) expects empty-row drags to fall through to it — and they did before the Flickable was added. Starting a marquee drag over morph lanes was therefore silently dropped. Fix: match `rowsView`'s `interactive: false` so left-drags pass through. Wheel scrolling still works because `scrollByPixels` (invoked by the root WheelHandler) now also drives the morph list's `contentY` when its content overflows — same dy as the bone list so one wheel notch advances both consistently. Co-Authored-By: Claude Opus 4.7 (1M context) --- qml/AnimationDopeSheet.qml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/qml/AnimationDopeSheet.qml b/qml/AnimationDopeSheet.qml index 95d6bea2..5dd456d0 100644 --- a/qml/AnimationDopeSheet.qml +++ b/qml/AnimationDopeSheet.qml @@ -690,9 +690,22 @@ Rectangle { // QQuickWidget inside QDockWidget can swallow wheel events on macOS; // routing them through here guarantees the rows scroll regardless. function scrollByPixels(dy) { - if (dy === 0 || !rowsView.visible) return - var maxY = Math.max(0, rowsView.contentHeight - rowsView.height) - rowsView.contentY = Math.max(0, Math.min(maxY, rowsView.contentY - dy)) + if (dy === 0) return + // Scroll the bone list as before; ListView handles its own + // clamping in contentY assignments below. + if (rowsView.visible) { + var maxY = Math.max(0, rowsView.contentHeight - rowsView.height) + rowsView.contentY = Math.max(0, Math.min(maxY, rowsView.contentY - dy)) + } + // Also scroll the morph band when it has overflowing content. + // We disable Flickable.interactive (so drag-marquee passes + // through to timelineArea) which removes Flickable's own wheel + // handling — proxy it here. Same dy as the bone list so a single + // wheel notch advances both views consistently. + if (morphBand.visible && morphList.contentHeight > morphList.height) { + var maxMY = morphList.contentHeight - morphList.height + morphList.contentY = Math.max(0, Math.min(maxMY, morphList.contentY - dy)) + } } WheelHandler { @@ -773,6 +786,13 @@ Rectangle { clip: true contentHeight: morphCol.height boundsBehavior: Flickable.StopAtBounds + // Match `rowsView` and disable left-drag flicking — the + // root timelineArea owns marquee selection over empty + // pixels and Flickable would otherwise grab those drags. + // Wheel scrolling still works because the root WheelHandler + // is routed through `scrollByPixels` rather than relying on + // Flickable's own wheel handling. + interactive: false Column { id: morphCol