Fix drag-to-refile: ListView stole the grip's drag

The grip used a bare MouseArea for the drag. Inside the ListView (a
Flickable), once the pointer moved past the drag threshold the ListView
stole the gesture for flicking, so the grip's positionChanged stopped
firing: the drop target was never resolved and release did nothing.

Replace the MouseArea with a DragHandler (target: null — it tracks, it
doesn't move the item) whose grabPermissions take the pointer grab and
refuse to yield it to the Flickable, so the drag is no longer stolen. It
reports centroid.scenePosition continuously, mapped to ListView content
coordinates via contentItem.mapFromScene, driving the same updateDrop/
commitDrop path. A HoverHandler keeps the hover affordance, and the grip
gets a slightly larger hit target.

QML compiles (AOT cache build of OutlineView). The drop resolution and
refile commit paths were already verified; this restores the gesture
that feeds them.
This commit is contained in:
Levi Neely 2026-10-02 16:57:18 +02:00
parent 1033820229
commit 0a06ca60bf
1 changed files with 34 additions and 18 deletions

View File

@ -520,33 +520,49 @@ ListView {
// Drag grip: drag a section (and its subtree) to restructure.
// A live indicator shows the resolved drop target; releasing
// commits a refile. Horizontal position chooses the depth,
// vertical position chooses the gap.
// vertical position chooses the gap. A DragHandler is used
// (not a MouseArea) so the enclosing ListView cannot steal the
// drag for flicking.
Label {
id: gripLabel
text: "⠿"
color: theme.placeholderTextColor
// A slightly larger hit target than the bare glyph.
Layout.preferredWidth: outline.baseFontSize + 6
horizontalAlignment: Text.AlignHCenter
font.pointSize: outline.baseFontSize - 1
opacity: gripMouse.containsMouse || outline.dragRow === del.index ? 1.0 : 0.3
opacity: gripHover.hovered || outline.dragRow === del.index ? 1.0 : 0.3
MouseArea {
id: gripMouse
anchors.fill: parent
hoverEnabled: true
HoverHandler {
id: gripHover
cursorShape: Qt.SizeAllCursor
preventStealing: true
onPressed: {
outline.dragRow = del.index
}
DragHandler {
id: gripDrag
target: null // we track, we don't move the item
dragThreshold: 3
// Take the grab and refuse to yield it to the ListView
// (a Flickable), which would otherwise steal the drag.
grabPermissions: PointerHandler.CanTakeOverFromItems
| PointerHandler.CanTakeOverFromHandlersOfDifferentType
onActiveChanged: {
if (active) {
outline.dragRow = del.index
} else {
outline.commitDrop()
}
}
onPositionChanged: (mouse) => {
if (outline.dragRow < 0) return
// Map the pointer to ListView content coordinates.
var p = mapToItem(outline.contentItem, mouse.x, mouse.y)
onCentroidChanged: {
if (!active || outline.dragRow < 0) return
// Map the scene pointer position to ListView content
// coordinates and resolve the drop.
var p = outline.contentItem.mapFromScene(
centroid.scenePosition.x,
centroid.scenePosition.y)
outline.updateDrop(p.x, p.y)
}
onReleased: outline.commitDrop()
onCanceled: {
outline.dragRow = -1
outline.dropInfo = null
}
}
}