Underestanding QML for D&D
-
I wanted to get Drag&Drop working in a way that used the drop target to get the desired new index. I think I got things working, but the exercised raised several questions.
- The docs say that
onDroppedgets aDragEventas an argument, and its documentation https://doc.qt.io/qt-5/qml-qtquick-dragevent.html says the event has adrag.sourceproperty. Code that attempts to reference it gets an error thatdragis undefined, and the object browser in the debugger shows no such attribute. However, it does show asourceattribute, which seemed to work. It may be relevant that the context was the function defined foronDropped(below), i.e.,dragEvt.source.drag.source. - https://doc.qt.io/qt-5/qml-qtquick-draghandler.html says "At this time, drag-and-drop is not yet supported." Huh?
- In the drop handler I wanted to access a child of the object being dragged, somewhat like the commented out line in the code below. But the indicated code fails because
draggeris not a member of the source. If I useddraggerwithout any qualifiers in front of it I suspect it might compile, but in the best case I assume that would get me the dragger associated with the target, not the source. Is there a way to achieve what I want? Well, usingoriginalIndexactually seemed to work in this case, but how can I navigate these hierarchies when a solution isn't pre-made? - Any other comments welcome; I'm just learning this.
Using Qt 5.11.3 on Debian GNU/Linux buster. Here's the code I have so far:
import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 ApplicationWindow { visible: true width: 500 height: 480 title: qsTr("Scroll") ListModel { id: categories ListElement { name: "Produce"} ListElement { name: "Bread"} ListElement { name: "Meat"} } ScrollView { anchors.fill: parent id: root ListView { width: parent.width id: theListView model: categories y: 400 delegate: Rectangle { id: rectangle width: Math.min(parent.width, 300) height: textEdit.height color: Qt.hsva(index/categories.count, .8, 1, .8) property int originalIndex: index Drag.active: dragger.drag.active Drag.hotSpot.x: width/2 Drag.hotSpot.y: height/2 Drag.dragType: Drag.Automatic Drag.supportedActions: Qt.MoveAction Drag.mimeData: { "text/plain": "not really text" } DropArea { anchors.fill: parent property int index: rectangle.originalIndex onDropped: function(dragEvt){ // dragEvt.drag is undefined below // but the undocumented DragEvent.source is present if (containsDrag && dragEvt.source !== undefined){ // dragEvt.source.dragger.oldIndex was my attempt to get at // information in a child of the source categories.move(dragEvt.source.originalIndex, index, 1) dragEvt.source.parent = theListView dragEvt.accept() } } } RowLayout{ Rectangle { id: draghandle Layout.fillWidth: true Layout.fillHeight: true Layout.preferredWidth: 15 Layout.preferredHeight: parent.height color: "yellow" opacity: 0.5 MouseArea { id: dragger anchors.fill: parent hoverEnabled: true property int oldIndex onEntered: parent.color = "black" onExited: parent.color = "yellow" onPressed: { oldIndex = index drag.target = rectangle drag.axis = Drag.YAxis rectangle.parent = root } } } TextEdit { id: textEdit Layout.fillWidth: true text: name font.pixelSize: 14 opacity: 0.6 } } } } } } - The docs say that
-
I found drag and drop to be finicky. If you haven't already seen this tutorial I would look at it. It helped me understand how to cleanly setup my drags and drops. I still found issues I had to tweak to make work just right. Like dragging items from a list would sometimes leave the source miss-adjusted. I had to tell it to remember its position to fix that. Other times I refreshed the list.