Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I've tried to implement drag and drop in Qt 5.3 using the new QML types Drag, DragEvent and DropArea. This is the original example from the documentation of the QML Drag type with some small modifications:

import QtQuick 2.2

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"

        Drag.active: dragArea.drag.active
        // Drag.dragType: Drag.Automatic
        Drag.onDragStarted: print("drag started");
        Drag.onDragFinished: print("drag finished");

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

Expected behaviour: The small blue rectangle (drag target) can be dragged around with the mouse. If dragged over the larger green rectangle in the center of the window, this rectangle turns red and back to green when leaving. In addition, the signals dragStarted, entered, exited, dropped and dragFinished are emitted in time and the corresponding signal handlers print out their messages.

Experienced behaviour:

Depends on Drag.dragType (see commented line above):

  1. Drag.dragType is NOT set (default is Drag.Internal):

    Drag and drop works as described, but only the signals entered and exited are emitted. The other signals (dragStarted, dragFinished and dropped) are suppressed. So there is no way to react to the drop in the DropArea.

  2. Drag.dragType is set to Drag.Automatic:

    All of the signals are emitted now, but the blue rectangle (drag target) does not move with the mouse. Instead, the mouse cursor changes its shape to visualize possible drop targets. After the mouse has been released, the blue rectangle jumps to the latest mouse position.

Neither of these two variants are pleasing. How can I get all signals and still be able to drag around the drag target? Unfortunately the documentation is everything but clear about drag-and-drop in QML, especially about the ominous Drag.dragType.

like image 638
isnot2bad Avatar asked Jul 02 '14 13:07

isnot2bad


1 Answers

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.
like image 130
MrEricSir Avatar answered Sep 28 '22 05:09

MrEricSir