Using drag and drop with a ListView to create an inventory UI

I would like to create an inventory UI for my game using a ListView where items can be removed from the inventory by dragging and dropping them onto the level. If the item has not dropped properly (still inside the inventory), it should be placed where it was before being dragged.

I have the following code, but I don't know how to achieve what I need, even by 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
        }
    }
}

      

+3


source to share


1 answer


This can be done 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;
            }
        }
    }
}

      



In this example, you can notice some things:

  • We save dragItemIndex

    so we know which element is being dragged. Perhaps we can achieve the same by looking at the DropArea drag.source property , but then we would need to open the index property in the delegate, and <documentation href = "http://qt-project.org/doc/qt-5/qml -qtquick-listview.html # example-usage "> prevents state from being persisted in delegates.

  • To ensure that the "items are returned to where they were on unsuccessful drags" function, we make the dragRect

    actual delegate item a child so that it has a parent. If we didn't, the parent of the item was the ListView, and on unsuccessful failure, it just lay where it was last time.

  • We use the same state changing behavior as in the drag and drop example ; when dragging, we want to remove the anchors from the element and allow it to move freely. If the drag fails, the condition when

    for state ( dragRect.Drag.active

    ) becomes false, and the item falls back to the delegate item, which does not move from its original location as a list. Anchors are also fixed. This is a useful function of states; the ability to implicitly restore the previous state.

  • In the signal handler, MouseArea

    drag.onActiveChanged

    we call dragRect.Drag.drop()

    so that we can respond to this event in the signal handler DropArea

    onDropped

    and remove the element. After deleting the item, we reset it dragItemIndex

    to an invalid index.

+6


source







All Articles