New drag-and-drop mechanism does not work as expected in Qt-Quick (Qt 5.3)

If you open the QQuickDrag source code and look at the differences between start(), which is used by Drag.Internal, and startDrag() which is used by Drag.Automatic, the difference is pretty obvious. start() sets up an event change listener, which it then uses to update the position of the attached object. startDrag() doesn't do this.

Why does it work this way? I have no idea! The QtQuick 2 drag and drop documentation certainly has room for improvement here.

There is a fairly simple workaround: take the best from both worlds. Use Drag.Automatic, but instead of setting Drag.active, call start() and drop() manually. It won't invoke Drag.onDragStarted() and Drag.onDragFinished() but you essentially get those for free anyway by listening for a change in the MouseArea's drag.active.

Here's the concept in action:

import QtQuick 2.0

Item {
    width: 800; height: 600

    DropArea {
        width: 100; height: 100; anchors.centerIn: parent

        Rectangle {
            anchors.fill: parent
            color: parent.containsDrag ? "red" : "green"
        }

        onEntered: print("entered");
        onExited: print("exited");
        onDropped: print("dropped");
    }

    Rectangle {
        x: 15; y: 15; width: 30; height: 30; color: "blue"

        // I've added this property for simplicity's sake.
        property bool dragActive: dragArea.drag.active

        // This can be used to get event info for drag starts and 
        // stops instead of onDragStarted/onDragFinished, since
        // those will neer be called if we don't use Drag.active
        onDragActiveChanged: {
            if (dragActive) {
                print("drag started")
                Drag.start();
            } else {
                print("drag finished")
                Drag.drop();
            }
        }

        Drag.dragType: Drag.Automatic

        // These are now handled above.
        //Drag.onDragStarted: print("drag started");
        //Drag.onDragFinished: print("drag finished");

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent
        }
    }
}

I realize it's not a completely satisfying solution, but it does match your expected behavior.

This solution offers:

  • Notifications for all of the desired events: drag started, drag finished, enter drag area, exit drag area, and dropped in drag area.
  • The drag animation is automatically handled by QtQuick. The square doesn't freeze in place like it does when running the sample code with Drag.Automatic.

What it doesn't offer:

  • An explanation as to why QtQuick's drag and drop functionality works this way, or whether it's even the intended behavior by the developers. The current documentation seems ambiguous.

Just ran into this myself (using Qt 5.2, but the same problem exists there). I've got a 'slider box' on the X-axis and just wanted to know when the drag was finished... instead of responding to every position change along the way. My workaround involved hacking the states/transitions, with a ScriptAction to provide the logic. This is the simplified version for mimicking a response to the "onDragFinished" signal. So while it doesn't cover all your drag/drop signals, it might get you pointed in the right direction.

Rectangle {
    id: sliderControl

    height: coordinates.height
    width: 80
    color: "#F78181"
    border.color: "#FE2E2E"
    border.width: 1
    opacity: 0.4

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        drag.target: sliderControl
        drag.axis: Drag.XAxis
        drag.minimumX: 0
        drag.maximumX: view.width - sliderControl.width
        hoverEnabled: true
    }

    states: [
        State {
            name: "dragging"
            when: mouseArea.drag.active
        },
        State {
            name: "finished_dragging"
            when: !mouseArea.drag.active
        }
    ]

    transitions: [
        Transition {
            from: "dragging"
            to: "finished_dragging"
            ScriptAction {
                script: console.log("finished dragging script");
            }
        }
    ]
}

ps - I know that such a 'workaround' doesn't qualify for the bounty parameters, but I was pretty bummed to find only your question (no solutions) when I searched for help on the issue. Hopefully anyone else stumbling down this path will find this useful. Unfortunately, I've got no clue what's going on with QML's Drag.dragType either.