I'd like to create an inventory UI for my game using ListView, where the items can be removed from the inventory by dragging and dropping them in the level. If an item was not dropped properly (still inside the inventory), it should be placed back where it was before the drag.
I've got the following code, but I don't know how to achieve what I'm after, even after looking at the Drag and Drop example.
import QtQuick 2.3
Rectangle {
id: root
width: 400
height: 400
ListView {
id: listView
width: parent.width / 2
height: parent.height
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 10; ++i) {
append({value: i});
}
}
}
delegate: Item {
id: delegateItem
width: listView.width
height: 50
Rectangle {
id: dragRect
width: listView.width
height: 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "salmon"
border.color: Qt.darker(color)
Text {
anchors.centerIn: parent
text: modelData
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: dragRect
}
Drag.hotSpot.x: dragRect.width / 2
Drag.hotSpot.y: dragRect.height / 2
}
}
}
Rectangle {
width: parent.width / 2
height: parent.height
anchors.right: parent.right
color: "#aaff0011"
DropArea {
id: dropArea
anchors.fill: parent
}
}
}
You can achieve this with the following code:
import QtQuick 2.3
Rectangle {
id: root
width: 400
height: 400
ListView {
id: listView
width: parent.width / 2
height: parent.height
property int dragItemIndex: -1
model: ListModel {
Component.onCompleted: {
for (var i = 0; i < 10; ++i) {
append({value: i});
}
}
}
delegate: Item {
id: delegateItem
width: listView.width
height: 50
Rectangle {
id: dragRect
width: listView.width
height: 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "salmon"
border.color: Qt.darker(color)
Text {
anchors.centerIn: parent
text: modelData
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: dragRect
drag.onActiveChanged: {
if (mouseArea.drag.active) {
listView.dragItemIndex = index;
}
dragRect.Drag.drop();
}
}
states: [
State {
when: dragRect.Drag.active
ParentChange {
target: dragRect
parent: root
}
AnchorChanges {
target: dragRect
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
}
}
]
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: dragRect.width / 2
Drag.hotSpot.y: dragRect.height / 2
}
}
}
Rectangle {
width: parent.width / 2
height: parent.height
anchors.right: parent.right
color: "#aaff0011"
DropArea {
id: dropArea
anchors.fill: parent
onDropped: {
listView.model.remove(listView.dragItemIndex);
listView.dragItemIndex = -1;
}
}
}
}
Some things to notice in this example:
We store the dragItemIndex
so that we know which item is being dragged. We might be able to achieve the same thing by looking at DropArea's drag.source property, but then we'd have to expose an index property in the delegate, and the documentation discourages storing state in delegates.
To achieve the "items get placed back where they were on unsuccessful drags" functionality, we make dragRect
a child of the actual delegate item, so that it has a parent to stick to. If we didn't do this, the item's parent would be the ListView, and when unsuccessfully dropped, it would just lay where it was last dragged.
We use the same state change behaviour as the Drag and Drop example; upon dragging we want to remove the anchors from the item and let it be dragged freely. If the drag fails, the when
condition for the state (dragRect.Drag.active
) becomes false, and the item is parented back to the delegate item that hasn't moved from its original place in the list view. The anchors are also restored. This is a useful feature of states; being able to implicitly restore previous state.
In the MouseArea
's drag.onActiveChanged
signal handler, we call dragRect.Drag.drop()
so that we can respond to that event in DropArea
's onDropped
signal handler, and remove the item. After the item has been removed, we reset the dragItemIndex
to an invalid index.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With