Dat8StringGuy
Dat8StringGuy

Reputation: 341

Drag and dropping identical QML components on top of each other

I have a repeater that generates a bunch of rectangles. I want to be able to drag these rectangles around and generate an event when I drop one rectangle on another (think files and folders on an OS. I can drag folders & files, but I can drop a file on a folder and it stores it inside). I've tried the following but the dropArea gets confused because the object itself is draggable. OnDropped just never triggers. Is this possible?

Repeater {
            model: 5
            Item {
                id: element

                Item {
                    id: surfaceContainer

                    width: 150
                    height: 150
                    x: index * width

                    DropArea {
                        id: dropArea

                        anchors.fill: surfaceContainer

                        onContainsDragChanged: {
                            if (containsDrag && drag.source != draggableRectangle)
                                console.log("CHANGED")
                        }

                        onEntered: {
                            if (drag.source != draggableRectangle)
                                console.log("ENTERED")
                        }

                        onDropped: {
                            console.log("DROPPED")
                        }

                        onExited: {
                            if (drag.source != draggableRectangle && containsDrag)
                                drag.source.color = "yellow"
                        }
                    }

                    Rectangle {
                        id: draggableRectangle

                        x: width / 2

                        color: "blue"
                        height: 100
                        width: 100

                        Drag.active: dragArea.drag.active

                        MouseArea {
                            id: dragArea

                            anchors.fill: draggableRectangle
                            drag.target: draggableRectangle

                            onDoubleClicked: {
                                connectionId.visible = !connectionId.visible
                            }
                        }
                    }
                }
            }
        }

Upvotes: 0

Views: 352

Answers (1)

Stephen Quan
Stephen Quan

Reputation: 25871

[EDIT]

I made the following changes to your application:

  • Initialize the DropArea to match the Rectangle geometry
  • Everytime the Rectangle has completed dragging, to re-update the DropArea to match the Rectangle geometry
  • When an Rectangle is being dragged, only enable the DropArea that are valid destinations, i.e. it can only be dragged to a DropArea of a different delegate
import QtQuick
import QtQuick.Controls
Page {
    property var sourceItem: null
    property var destItem: null
    property string statusText: ""
    Repeater {
        model: [ "A", "B", "C", "D", "E" ]
        Item {
            z: dragArea.drag.active ? 2 : 0
            DropArea {
                id: dropArea
                x: index * 150 + 100
                y: 100
                width: 100
                height: 100
                enabled: sourceItem && sourceItem !== modelData
                onContainsDragChanged: {
                    if (containsDrag) {
                        destItem = modelData;
                    } else {
                        destItem = null;
                    }
                }
            }
            Rectangle {
                id: dragRect
                x: index * 150 + 100
                y: 100
                width: 100
                height: 100
                color: dropArea.containsDrag ? "green" : dragArea.drag.active ? "#c00" : "#f88"
                Drag.active: dragArea.drag.active
                Drag.hotSpot.x: 10
                Drag.hotSpot.y: 10
                Label {
                    anchors.centerIn: parent
                    text: modelData
                }
                MouseArea {
                    id: dragArea
                    anchors.fill: parent
                    
                    drag.target: parent
                    drag.onActiveChanged: {
                    if (drag.active) {
                        sourceItem = modelData;
                    } else {
                        if (sourceItem && destItem) {
                            statusText = "Dragged " + JSON.stringify(sourceItem) + " to " + JSON.stringify(destItem);
                        } else {
                            statusText = "";
                        }
                        sourceItem = null;
                        dropArea.x = dragRect.x;
                        dropArea.y = dragRect.y; 
                    }
}
                }
            }
        }
    }
    Frame {
        anchors.horizontalCenter: parent.horizontalCenter
        y: parent.height * 3 / 4
        visible: statusText
        Text {
            text: statusText
        }
    }
}

You can Try it Online!

Upvotes: 1

Related Questions