diff --git a/crates/qbz-slint/ui/label/LabelPageView.slint b/crates/qbz-slint/ui/label/LabelPageView.slint index 9850f560e..e8496ef01 100644 --- a/crates/qbz-slint/ui/label/LabelPageView.slint +++ b/crates/qbz-slint/ui/label/LabelPageView.slint @@ -12,7 +12,7 @@ import { Theme } from "../foundation/semantic-colors.slint"; import { Typography } from "../foundation/typography.slint"; import { Radius } from "../foundation/radius.slint"; -import { LabelState, LabelActions, TrackItem } from "../state.slint"; +import { LabelState, LabelActions, TrackItem, DragState } from "../state.slint"; import { QbzIcon } from "../primitives/QbzIcon.slint"; import { TrackRow } from "../primitives/TrackRow.slint"; import { CircleAction } from "../primitives/CircleAction.slint"; @@ -47,6 +47,9 @@ export component LabelPageView inherits Rectangle { background: Theme.surface-main; page-flickable := Flickable { + // Don't scroll while a track is being dragged out (keeps the row's + // pointer grab so the drag-end fires) — matches the other track views. + interactive: !DragState.active; viewport-width: self.width; viewport-height: page.preferred-height; diff --git a/crates/qbz-slint/ui/primitives/TrackRow.slint b/crates/qbz-slint/ui/primitives/TrackRow.slint index 1bebf0f12..a433e134d 100644 --- a/crates/qbz-slint/ui/primitives/TrackRow.slint +++ b/crates/qbz-slint/ui/primitives/TrackRow.slint @@ -105,6 +105,19 @@ export component TrackRow inherits Rectangle { ta.dragging = false; } } + // Safety net: the `up` above only arrives if this TouchArea still holds + // the pointer grab. Inside a ListView (or a Flickable that didn't drop + // out of `interactive`), the scroll gesture can steal the grab mid-drag, + // so the `up` never reaches us and the ghost stays glued to the cursor. + // `pressed` flips to false on BOTH a normal release and a grab cancel, + // so ending the drag here guarantees it always clears. Both paths guard + // on `ta.dragging`, so whichever fires first wins and end() runs once. + changed pressed => { + if (!self.pressed && ta.dragging) { + DragActions.end(); + ta.dragging = false; + } + } moved => { if (ta.pressed && root.draggable) { if (!ta.dragging