Add a DragTarget element.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Thu, 9 Jun 2011 08:29:50 +0000 (18:29 +1000)
committerAndrew den Exter <andrew.den-exter@nokia.com>
Thu, 9 Jun 2011 08:29:50 +0000 (18:29 +1000)
Provides an area that can be used to handle events when other items are
dragged over it.

Task-number: QMLNG-32

21 files changed:
examples/declarative/dragtarget/dragtarget.qmlproject [new file with mode: 0644]
examples/declarative/dragtarget/lists/listmodel.qml [new file with mode: 0644]
examples/declarative/dragtarget/lists/lists.qmlproject [new file with mode: 0644]
examples/declarative/dragtarget/text/dragtext.qml [new file with mode: 0644]
examples/declarative/dragtarget/text/text.qmlproject [new file with mode: 0644]
examples/declarative/dragtarget/tiles/DragTile.qml [new file with mode: 0644]
examples/declarative/dragtarget/tiles/DropTile.qml [new file with mode: 0644]
examples/declarative/dragtarget/tiles/tiles.qml [new file with mode: 0644]
src/declarative/items/items.pri
src/declarative/items/qsgcanvas.cpp
src/declarative/items/qsgcanvas_p.h
src/declarative/items/qsgdragtarget.cpp [new file with mode: 0644]
src/declarative/items/qsgdragtarget_p.h [new file with mode: 0644]
src/declarative/items/qsgevent.h [new file with mode: 0644]
src/declarative/items/qsgitem.cpp
src/declarative/items/qsgitem.h
src/declarative/items/qsgitem_p.h
src/declarative/items/qsgitemsmodule.cpp
src/declarative/items/qsgmousearea.cpp
src/declarative/items/qsgmousearea_p.h
src/declarative/items/qsgmousearea_p_p.h

diff --git a/examples/declarative/dragtarget/dragtarget.qmlproject b/examples/declarative/dragtarget/dragtarget.qmlproject
new file mode 100644 (file)
index 0000000..d4909f8
--- /dev/null
@@ -0,0 +1,16 @@
+import QmlProject 1.0
+
+Project {
+    /* Include .qml, .js, and image files from current directory and subdirectories */
+    QmlFiles {
+        directory: "."
+    }
+    JavaScriptFiles {
+        directory: "."
+    }
+    ImageFiles {
+        directory: "."
+    }
+    /* List of plugin directories passed to QML runtime */
+    // importPaths: [ " ../exampleplugin " ]
+}
diff --git a/examples/declarative/dragtarget/lists/listmodel.qml b/examples/declarative/dragtarget/lists/listmodel.qml
new file mode 100644 (file)
index 0000000..50b1d39
--- /dev/null
@@ -0,0 +1,256 @@
+import QtQuick 2.0
+
+
+Rectangle {
+    id: root
+    color: "grey"
+
+    width: 720
+    height: 380
+
+    Component {
+        id: draggedText
+        Text {
+            x: rootTarget.dragX - 10
+            y: rootTarget.dragY - 10
+            width: 20
+            height: 20
+
+            text: rootTarget.dragData.display
+            font.pixelSize: 18
+        }
+    }
+
+    DragTarget {
+        id: rootTarget
+
+        anchors.fill: parent
+    }
+
+    Loader {
+        anchors.fill: parent
+        sourceComponent: rootTarget.containsDrag ? draggedText : undefined
+    }
+
+    GridView {
+        id: gridView
+
+        width: 240
+        height: 360
+
+        anchors.left: parent.left
+        anchors.verticalCenter: parent.verticalCenter
+        anchors.margins: 10
+
+        cellWidth: 60
+        cellHeight: 60
+
+        model: ListModel {
+            id: gridModel
+
+            ListElement { display: "1" }
+            ListElement { display: "2" }
+            ListElement { display: "3" }
+            ListElement { display: "4" }
+            ListElement { display: "5" }
+            ListElement { display: "6" }
+            ListElement { display: "7" }
+            ListElement { display: "8" }
+            ListElement { display: "9" }
+            ListElement { display: "10" }
+            ListElement { display: "11" }
+            ListElement { display: "12" }
+            ListElement { display: "13" }
+            ListElement { display: "14" }
+            ListElement { display: "15" }
+            ListElement { display: "16" }
+            ListElement { display: "17" }
+            ListElement { display: "18" }
+            ListElement { display: "19" }
+            ListElement { display: "20" }
+            ListElement { display: "21" }
+            ListElement { display: "22" }
+            ListElement { display: "23" }
+            ListElement { display: "24" }
+        }
+
+        delegate: Rectangle {
+            id: root
+
+            width: 60
+            height: 60
+
+            color: "black"
+
+            Text {
+                anchors.fill: parent
+                color: draggable.drag.active ? "gold" : "white"
+                text: display
+                font.pixelSize: 16
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+
+            MouseArea {
+                id: draggable
+
+                property int initialIndex
+
+                width: 60
+                height: 60
+
+                drag.data: model
+                drag.keys: ["grid"]
+                drag.target: draggable
+
+                states: State {
+                    when: !draggable.drag.active
+                    PropertyChanges { target: draggable; x: 0; y: 0 }
+                }
+            }
+        }
+
+        DragTarget {
+            anchors.fill: parent
+
+            keys: [ "grid" ]
+            onPositionChanged: {
+                var index = gridView.indexAt(drag.x, drag.y)
+                if (index != -1)
+                    gridModel.move(drag.data.index, index, 1)
+            }
+        }
+
+        DragTarget {
+            property int dragIndex
+            anchors.fill: parent
+
+            keys: [ "list" ]
+            onEntered: {
+                dragIndex = gridView.indexAt(drag.x, drag.y)
+                if (dragIndex != -1) {
+                    gridModel.insert(dragIndex, { "display": drag.data.display })
+                } else {
+                    event.accepted = false
+                }
+            }
+            onPositionChanged: {
+                var index = gridView.indexAt(drag.x, drag.y);
+                if (index != -1) {
+                    gridModel.move(dragIndex, index, 1)
+                    dragIndex = index
+                }
+            }
+            onExited: gridModel.remove(dragIndex, 1)
+        }
+    }
+
+    ListView {
+        id: listView
+
+        width: 240
+        height: 360
+
+        anchors.right: parent.right
+        anchors.verticalCenter: parent.verticalCenter
+        anchors.margins: 10
+
+        model: ListModel {
+            id: listModel
+
+            ListElement { display: "a" }
+            ListElement { display: "b" }
+            ListElement { display: "c" }
+            ListElement { display: "d"}
+            ListElement { display: "e" }
+            ListElement { display: "f" }
+            ListElement { display: "g" }
+            ListElement { display: "h" }
+            ListElement { display: "i" }
+            ListElement { display: "j" }
+            ListElement { display: "k" }
+            ListElement { display: "l" }
+            ListElement { display: "m" }
+            ListElement { display: "n" }
+            ListElement { display: "o" }
+            ListElement { display: "p" }
+            ListElement { display: "q" }
+            ListElement { display: "r" }
+            ListElement { display: "s" }
+            ListElement { display: "t" }
+            ListElement { display: "u" }
+            ListElement { display: "v" }
+            ListElement { display: "w" }
+            ListElement { display: "x" }
+        }
+
+        delegate: Rectangle {
+            id: root
+
+            width: 240
+            height: 15
+
+            color: "black"
+
+            Text {
+                anchors.fill: parent
+                color: draggable.drag.active ? "gold" : "white"
+                text: display
+                font.pixelSize: 12
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+
+            MouseArea {
+                id: draggable
+
+                width: 240
+                height: 15
+
+                drag.data: model
+                drag.keys: ["list"]
+                drag.target: draggable
+
+                states: State {
+                    when: !draggable.drag.active
+                    PropertyChanges { target: draggable; x: 0; y: 0 }
+                }
+            }
+        }
+
+        DragTarget {
+            anchors.fill: parent
+
+            keys: [ "list" ]
+            onPositionChanged: {
+                var index = listView.indexAt(drag.x, drag.y)
+                if (index != -1)
+                    listModel.move(drag.data.index, index, 1)
+            }
+        }
+
+        DragTarget {
+            property int dragIndex
+            anchors.fill: parent
+
+            keys: [ "grid" ]
+
+            onEntered: {
+                dragIndex = listView.indexAt(drag.x, drag.y)
+                if (dragIndex != -1) {
+                    listModel.insert(dragIndex, { "display": drag.data.display })
+                } else {
+                    event.accepted = false
+                }
+            }
+            onPositionChanged: {
+                var index = listView.indexAt(drag.x, drag.y);
+                if (index != -1) {
+                    listModel.move(dragIndex, index, 1)
+                    dragIndex = index
+                }
+            }
+            onExited: listModel.remove(dragIndex, 1)
+        }
+    }
+}
diff --git a/examples/declarative/dragtarget/lists/lists.qmlproject b/examples/declarative/dragtarget/lists/lists.qmlproject
new file mode 100644 (file)
index 0000000..d4909f8
--- /dev/null
@@ -0,0 +1,16 @@
+import QmlProject 1.0
+
+Project {
+    /* Include .qml, .js, and image files from current directory and subdirectories */
+    QmlFiles {
+        directory: "."
+    }
+    JavaScriptFiles {
+        directory: "."
+    }
+    ImageFiles {
+        directory: "."
+    }
+    /* List of plugin directories passed to QML runtime */
+    // importPaths: [ " ../exampleplugin " ]
+}
diff --git a/examples/declarative/dragtarget/text/dragtext.qml b/examples/declarative/dragtarget/text/dragtext.qml
new file mode 100644 (file)
index 0000000..c4a4f24
--- /dev/null
@@ -0,0 +1,142 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    width: 320; height: 480
+
+    Rectangle {
+        id: inputRect
+        anchors.left: parent.left; anchors.right: parent.right; anchors.top: parent.top
+        anchors.margins: 2
+        height: input.implicitHeight + 4
+
+        border.width: 1
+
+        TextInput {
+            id: input
+            anchors.fill: parent; anchors.margins: 2
+
+            text: "the quick brown fox jumped over the lazy dog"
+
+            DragTarget {
+                id: inputTarget
+
+                anchors.fill: parent
+
+                Component {
+                    id: draggedInputText
+                    Text {
+                        x: inputTarget.dragX
+                        y: inputTarget.dragY
+                        text: inputTarget.dragData
+                        color: "blue"
+                        font: input.font
+                    }
+                }
+
+                Loader {
+                    sourceComponent: parent.containsDrag ? draggedInputText : undefined
+                }
+            }
+
+
+            MouseArea {
+                id: inputDraggable
+
+                anchors.fill: parent
+                enabled: input.selectionStart != input.selectionEnd
+
+                drag.data: input.selectedText
+                drag.target: inputDraggable
+
+                drag.onDragged: {
+                    var position = input.positionAt(mouse.x);
+                    mouse.accepted = position >= input.selectionStart && position < input.selectionEnd
+                }
+
+                MouseArea {
+                    anchors.fill: parent
+
+                    onPressed: {
+                        var position = input.positionAt(mouse.x);
+                        if (position < input.selectionStart || position >= input.selectionEnd) {
+                            input.cursorPosition = position
+                        } else {
+                            mouse.accepted = false
+                        }
+                    }
+                    onPositionChanged: input.moveCursorSelection(input.positionAt(mouse.x))
+                }
+            }
+        }
+    }
+
+    Rectangle {
+        id: editRect
+        anchors.left: parent.left; anchors.right: parent.right;
+        anchors.top: inputRect.bottom; anchors.bottom: parent.bottom
+        anchors.margins: 2
+
+        border.width: 1
+
+        TextEdit {
+            id: edit
+            anchors.fill: parent; anchors.margins: 2
+
+            text: "the quick brown fox jumped over the lazy dog"
+            font.pixelSize: 18
+            wrapMode: TextEdit.WordWrap
+
+            DragTarget {
+                id: editTarget
+
+                anchors.fill: parent
+
+
+                Component {
+                    id: draggedEditText
+                    Text {
+                        x: editTarget.dragX
+                        y: editTarget.dragY
+                        text: editTarget.dragData
+                        color: "red"
+                        font: edit.font
+                    }
+                }
+
+                Loader {
+                    sourceComponent: parent.containsDrag ? draggedEditText : undefined
+                }
+            }
+
+            MouseArea {
+                id: editDraggable
+
+                anchors.fill: parent
+                enabled: edit.selectionStart != edit.selectionEnd
+
+                drag.data: edit.selectedText
+                drag.target: editDraggable
+
+                drag.onDragged: {
+                    var position = edit.positionAt(mouse.x, mouse.y);
+                    mouse.accepted = position >= edit.selectionStart && position < edit.selectionEnd
+                }
+
+                MouseArea {
+                    anchors.fill: parent
+
+                    onPressed: {
+                        var position = edit.positionAt(mouse.x, mouse.y);
+                        if (position < edit.selectionStart || position >= edit.selectionEnd) {
+                            edit.cursorPosition = position
+                        } else {
+                            mouse.accepted = false
+                        }
+                    }
+                    onPositionChanged: edit.moveCursorSelection(edit.positionAt(mouse.x, mouse.y))
+                }
+            }
+        }
+    }
+}
diff --git a/examples/declarative/dragtarget/text/text.qmlproject b/examples/declarative/dragtarget/text/text.qmlproject
new file mode 100644 (file)
index 0000000..d4909f8
--- /dev/null
@@ -0,0 +1,16 @@
+import QmlProject 1.0
+
+Project {
+    /* Include .qml, .js, and image files from current directory and subdirectories */
+    QmlFiles {
+        directory: "."
+    }
+    JavaScriptFiles {
+        directory: "."
+    }
+    ImageFiles {
+        directory: "."
+    }
+    /* List of plugin directories passed to QML runtime */
+    // importPaths: [ " ../exampleplugin " ]
+}
diff --git a/examples/declarative/dragtarget/tiles/DragTile.qml b/examples/declarative/dragtarget/tiles/DragTile.qml
new file mode 100644 (file)
index 0000000..213373a
--- /dev/null
@@ -0,0 +1,59 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: dragRectangle
+
+    property Item dropTarget
+
+    property string colorKey
+
+    color: colorKey
+
+    width: 100; height: 100
+
+    Text {
+        anchors.fill: parent
+        color: "white"
+        font.pixelSize: 90
+        text: modelData + 1
+        horizontalAlignment:Text.AlignHCenter
+        verticalAlignment: Text.AlignVCenter
+    }
+
+    MouseArea {
+        id: draggable
+
+        anchors.fill: parent
+
+        drag.target: parent
+        drag.keys: [ colorKey ]
+
+        drag.onDropped: dropTarget = dropItem
+
+        states: [
+            State {
+                when: dragRectangle.dropTarget != undefined && !draggable.drag.active
+                ParentChange {
+                    target: dragRectangle
+                    parent: dropTarget
+                    x: 0
+                    y: 0
+                }
+            },
+            State {
+                when: dragRectangle.dropTarget != undefined && draggable.drag.active
+                ParentChange {
+                    target: dragRectangle
+                    parent: dropTarget
+                }
+            },
+            State {
+                when:  !draggable.drag.active
+                AnchorChanges {
+                    target: dragRectangle
+                    anchors.horizontalCenter: parent.horizontalCenter
+                }
+            }
+        ]
+    }
+}
diff --git a/examples/declarative/dragtarget/tiles/DropTile.qml b/examples/declarative/dragtarget/tiles/DropTile.qml
new file mode 100644 (file)
index 0000000..9d96875
--- /dev/null
@@ -0,0 +1,30 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: dropRectangle
+
+    property string colorKey
+
+    color: colorKey
+
+    width: 100; height: 100
+
+    DragTarget {
+        id: dragTarget
+
+        anchors.fill: parent
+
+        keys: [ colorKey ]
+        dropItem: dropRectangle
+    }
+
+    states: [
+        State {
+            when: dragTarget.containsDrag
+            PropertyChanges {
+                target: dropRectangle
+                color: "grey"
+            }
+        }
+    ]
+}
diff --git a/examples/declarative/dragtarget/tiles/tiles.qml b/examples/declarative/dragtarget/tiles/tiles.qml
new file mode 100644 (file)
index 0000000..d8bcb39
--- /dev/null
@@ -0,0 +1,85 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: root
+
+    width: 620
+    height: 410
+
+    color: "black"
+
+    DragTarget {
+        id: resetTarget
+
+        anchors.fill: parent
+    }
+
+    Grid {
+        id: redDestination
+
+        anchors.left: redSource.right; anchors.top: parent.top;
+        anchors.margins: 5
+        width: 300
+        height: 300
+
+        opacity: 0.5
+
+        columns: 3
+
+        Repeater {
+            model: 9
+            delegate: DropTile {
+                colorKey: "red"
+            }
+        }
+    }
+
+    Grid {
+        id: blueDestination
+
+        anchors.right: blueSource.left; anchors.bottom: parent.bottom;
+        anchors.margins: 5
+        width: 300
+        height: 300
+
+        opacity: 0.5
+
+        columns: 3
+
+        Repeater {
+            model: 9
+            delegate: DropTile {
+                colorKey: "blue"
+            }
+        }
+    }
+
+    Column {
+        id: redSource
+
+        anchors.left: parent.left; anchors.top: parent.top; anchors.bottom: parent.bottom
+        anchors.margins: 5
+        width: 100
+
+        Repeater {
+            model: 9
+            delegate: DragTile {
+                colorKey: "red"
+            }
+        }
+    }
+    Column {
+        id: blueSource
+
+        anchors.right: parent.right; anchors.top: parent.top; anchors.bottom: parent.bottom
+        anchors.margins: 5
+        width: 100
+
+        Repeater {
+            model: 9
+            delegate: DragTile {
+                colorKey: "blue"
+            }
+        }
+    }
+}
index f29a82e..bf92025 100644 (file)
@@ -64,6 +64,8 @@ HEADERS += \
     $$PWD/qsgspriteengine_p.h \
     $$PWD/qsgsprite_p.h \
     $$PWD/qsgspriteimage_p.h \
+    $$PWD/qsgevent.h \
+    $$PWD/qsgdragtarget_p.h \
 
 SOURCES += \
     $$PWD/qsgevents.cpp \
@@ -106,6 +108,7 @@ SOURCES += \
     $$PWD/qsgspriteengine.cpp \
     $$PWD/qsgsprite.cpp \
     $$PWD/qsgspriteimage.cpp \
+    $$PWD/qsgdragtarget.cpp \
 
 SOURCES += \
     $$PWD/qsgshadereffectitem.cpp \
index f991609..3a88fbb 100644 (file)
@@ -45,6 +45,8 @@
 #include "qsgitem.h"
 #include "qsgitem_p.h"
 
+#include "qsgevent.h"
+
 #include <private/qsgrenderer_p.h>
 #include <private/qsgflashnode_p.h>
 
@@ -987,6 +989,12 @@ bool QSGCanvas::event(QEvent *e)
         d->clearHover();
         d->lastMousePosition = QPoint();
         break;
+    case QSGEvent::SGDragEnter:
+    case QSGEvent::SGDragExit:
+    case QSGEvent::SGDragMove:
+    case QSGEvent::SGDragDrop:
+        d->deliverDragEvent(static_cast<QSGDragEvent *>(e));
+        break;
     default:
         break;
     }
@@ -1446,6 +1454,78 @@ bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, con
     return false;
 }
 
+void QSGCanvasPrivate::deliverDragEvent(QSGDragEvent *event)
+{
+    Q_Q(QSGCanvas);
+    if (event->type() == QSGEvent::SGDragExit || event->type() == QSGEvent::SGDragDrop) {
+        if (QSGItem *grabItem = event->grabItem()) {
+            event->setPosition(grabItem->mapFromScene(event->scenePosition()));
+            q->sendEvent(grabItem, event);
+        }
+    } else if (!deliverDragEvent(rootItem, event)) {
+        if (QSGItem *grabItem = event->grabItem()) {
+            QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
+            exitEvent.setPosition(grabItem->mapFromScene(event->scenePosition()));
+            q->sendEvent(grabItem, &exitEvent);
+            event->setDropItem(0);
+            event->setGrabItem(0);
+        }
+        event->setAccepted(false);
+    }
+}
+
+bool QSGCanvasPrivate::deliverDragEvent(QSGItem *item, QSGDragEvent *event)
+{
+    Q_Q(QSGCanvas);
+    QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+    if (itemPrivate->opacity == 0.0)
+        return false;
+
+    if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+        QPointF p = item->mapFromScene(event->scenePosition());
+        if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+            return false;
+    }
+
+    QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+    for (int ii = children.count() - 1; ii >= 0; --ii) {
+        QSGItem *child = children.at(ii);
+        if (!child->isVisible() || !child->isEnabled())
+            continue;
+        if (deliverDragEvent(child, event))
+            return true;
+    }
+
+    QPointF p = item->mapFromScene(event->scenePosition());
+    if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+        event->setPosition(p);
+
+        if (event->type() == QSGEvent::SGDragMove && item != event->grabItem()) {
+            QSGDragEvent enterEvent(QSGEvent::SGDragEnter, *event);
+            q->sendEvent(item, &enterEvent);
+            if (enterEvent.isAccepted()) {
+                if (QSGItem *grabItem = event->grabItem()) {
+                    QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
+                    q->sendEvent(grabItem, &exitEvent);
+                }
+                event->setDropItem(enterEvent.dropItem());
+                event->setGrabItem(item);
+            } else {
+                return false;
+            }
+        }
+
+        q->sendEvent(item, event);
+        if (event->isAccepted()) {
+            event->setGrabItem(item);
+            return true;
+        }
+        event->setAccepted(true);
+    }
+
+    return false;
+}
+
 bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QGraphicsSceneMouseEvent *event)
 {
     if (!target)
@@ -1521,6 +1601,12 @@ bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e)
     case QEvent::TouchEnd:
         QSGItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
         break;
+    case QSGEvent::SGDragEnter:
+    case QSGEvent::SGDragExit:
+    case QSGEvent::SGDragMove:
+    case QSGEvent::SGDragDrop:
+        QSGItemPrivate::get(item)->deliverDragEvent(static_cast<QSGDragEvent *>(e));
+        break;
     default:
         break;
     }
index 90132f8..9b2683c 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "qsgitem.h"
 #include "qsgcanvas.h"
+#include "qsgevent.h"
 #include <private/qdeclarativeguard_p.h>
 
 #include <private/qsgcontext_p.h>
@@ -116,6 +117,8 @@ public:
     bool deliverHoverEvent(QSGItem *, QGraphicsSceneHoverEvent *);
     void sendHoverEvent(QEvent::Type, QSGItem *, QGraphicsSceneHoverEvent *);
     void clearHover();
+    void deliverDragEvent(QSGDragEvent *);
+    bool deliverDragEvent(QSGItem *item, QSGDragEvent *);
 
     QDeclarativeGuard<QSGItem> hoverItem;
     enum FocusOption {
diff --git a/src/declarative/items/qsgdragtarget.cpp b/src/declarative/items/qsgdragtarget.cpp
new file mode 100644 (file)
index 0000000..c1ed167
--- /dev/null
@@ -0,0 +1,361 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgdragtarget_p.h"
+#include "qsgitem_p.h"
+#include "qsgcanvas.h"
+
+/*!
+    \qmlclass DragEvent QSGDragEvent
+    \brief The DragEvent object provides information about a drag event.
+
+    The position of the drag event can be obtained from the \l x and \l
+    properties, the \l keys property identifies the drag keys of the event
+    source and the \l data property contains the payload of the drag event.
+*/
+
+/*!
+    \qmlproperty real DragEvent::x
+
+    This property holds the x coordinate of a drag event.
+*/
+
+/*!
+    \qmlproperty real DragEvent::y
+
+    This property holds the y coordinate of a drag event.
+*/
+
+/*!
+    \qmlproperty stringlist DragEvent::keys
+
+    This property holds a list of keys identifying the data type or source of a
+    drag event.
+*/
+
+/*!
+    \qmlproperty variant DragEvent::data
+
+    This property holds data payload of a drag event.
+*/
+
+/*!
+    \qmlproperty real DragEvent::accepted
+
+    This property holds whether the drag event was accepted by a handler.
+
+    The default value is true.
+*/
+
+class QSGDragTargetPrivate : public QSGItemPrivate
+{
+    Q_DECLARE_PUBLIC(QSGDragTarget)
+
+public:
+    QSGDragTargetPrivate();
+    ~QSGDragTargetPrivate();
+
+    bool hasMatchingKey(const QStringList &keys) const;
+
+    QStringList keys;
+    QRegExp keyRegExp;
+    QVariant dragData;
+    QPointF dragPosition;
+    QSGItem *dropItem;
+    bool containsDrag : 1;
+};
+
+QSGDragTargetPrivate::QSGDragTargetPrivate()
+    : dropItem(0)
+    , containsDrag(false)
+{
+}
+
+QSGDragTargetPrivate::~QSGDragTargetPrivate()
+{
+}
+
+/*!
+    \qmlclass DragTarget QSGDragTarget
+    \brief The DragTarget item provides drag and drop handling.
+
+    A DragTarget is an invisible item which receives events when another item
+    is dragged over it.
+
+    A MouseArea item can be used to drag items.
+
+    The \l keys property can be used to filter drag events which don't include
+    a matching key.
+
+    The \l dropItem property is communicated to the source of a drag event as
+    the recipient of a drop on the drag target.
+
+    The \l delegate property provides a means to specify a component to be
+    instantiated for each active drag over a drag target.
+*/
+
+QSGDragTarget::QSGDragTarget(QSGItem *parent)
+    : QSGItem(*new QSGDragTargetPrivate, parent)
+{
+}
+
+QSGDragTarget::~QSGDragTarget()
+{
+}
+
+/*!
+    \qmlproperty bool DragTarget::containsDrag
+
+    This property identifies whether the DragTarget currently contains any
+    dragged items.
+*/
+
+bool QSGDragTarget::containsDrag() const
+{
+    Q_D(const QSGDragTarget);
+    return d->containsDrag;
+}
+
+/*!
+    \qmlproperty stringlist DragTarget::keys
+
+    This property holds a list of drag keys a DragTarget will accept.
+*/
+
+QStringList QSGDragTarget::keys() const
+{
+    Q_D(const QSGDragTarget);
+    return d->keys;
+}
+
+void QSGDragTarget::setKeys(const QStringList &keys)
+{
+    Q_D(QSGDragTarget);
+    if (d->keys != keys) {
+        d->keys = keys;
+
+        if (keys.isEmpty()) {
+            d->keyRegExp = QRegExp();
+        } else {
+            QString pattern = QLatin1Char('(') + QRegExp::escape(keys.first());
+            for (int i = 1; i < keys.count(); ++i)
+                pattern += QLatin1Char('|') + QRegExp::escape(keys.at(i));
+            pattern += QLatin1Char(')');
+            d->keyRegExp = QRegExp(pattern.replace(QLatin1String("\\*"), QLatin1String(".+")));
+        }
+        emit keysChanged();
+    }
+}
+
+/*!
+    \qmlproperty Item DragTarget::dropItem
+
+    This property identifies an item as the recipient of a drop event within
+    a DragTarget.
+
+    \sa MouseArea::drag.dropItem
+*/
+
+QSGItem *QSGDragTarget::dropItem() const
+{
+    Q_D(const QSGDragTarget);
+    return d->dropItem;
+}
+
+void QSGDragTarget::setDropItem(QSGItem *item)
+{
+    Q_D(QSGDragTarget);
+    if (d->dropItem != item) {
+        d->dropItem = item;
+        emit dropItemChanged();
+    }
+}
+
+void QSGDragTarget::resetDropItem()
+{
+    Q_D(QSGDragTarget);
+    if (d->dropItem) {
+        d->dropItem = 0;
+        emit dropItemChanged();
+    }
+}
+
+qreal QSGDragTarget::dragX() const
+{
+    Q_D(const QSGDragTarget);
+    return d->dragPosition.x();
+}
+
+qreal QSGDragTarget::dragY() const
+{
+    Q_D(const QSGDragTarget);
+    return d->dragPosition.y();
+}
+
+QVariant QSGDragTarget::dragData() const
+{
+    Q_D(const QSGDragTarget);
+    return d->dragData;
+}
+
+/*!
+    \qmlsignal DragTarget::onPositionChanged(DragEvent drag)
+    \qmlattachedsignal DragTarget::onPositionChanged(DragEvent drag)
+
+    This handler is called when the position of a drag has changed.
+*/
+
+void QSGDragTarget::dragMoveEvent(QSGDragEvent *event)
+{
+    Q_D(QSGDragTarget);
+    if (!d->containsDrag) {
+        event->setAccepted(false);
+        return;
+    }
+
+    event->setDropItem(d->dropItem);
+
+    d->dragPosition = event->position();
+    emit dragPositionChanged();
+
+    QSGDragTargetEvent dragTargetEvent(event);
+    emit positionChanged(&dragTargetEvent);
+}
+
+bool QSGDragTargetPrivate::hasMatchingKey(const QStringList &keys) const
+{
+    if (keyRegExp.isEmpty())
+        return true;
+
+    foreach (const QString &key, keys) {
+        if (keyRegExp.exactMatch(key))
+            return true;
+    }
+    return false;
+}
+
+/*!
+    \qmlsignal DragTarget::onEntered(DragEvent drag)
+    \qmlattachedsignal DragTarget::onEntered(DragEvent drag)
+
+    This handler is called when a drag enters the bounds of a DragTarget.
+*/
+
+void QSGDragTarget::dragEnterEvent(QSGDragEvent *event)
+{
+    Q_D(QSGDragTarget);
+    if (!d->effectiveEnable || !d->hasMatchingKey(event->keys()) || d->containsDrag) {
+        event->setAccepted(false);
+        return;
+    }
+
+    event->setDropItem(d->dropItem);
+
+    QSGDragTargetEvent dragTargetEvent(event);
+    emit entered(&dragTargetEvent);
+
+    if (event->isAccepted()) {
+
+        d->dragData = event->data();
+        d->containsDrag = true;
+        if (!d->dragData.isNull())
+            emit dragDataChanged();
+        emit containsDragChanged();
+    }
+}
+
+/*!
+    \qmlsignal DragTarget::onExited(DragEvent drag)
+    \qmlattachedsignal DragTarget::onExited(DragEvent drag)
+
+    This handler is called when a drag exits the bounds of a DragTarget.
+*/
+
+void QSGDragTarget::dragExitEvent(QSGDragEvent *event)
+{
+    Q_D(QSGDragTarget);
+    if (!d->containsDrag) {
+        event->setAccepted(false);
+        return;
+    }
+
+    QSGDragTargetEvent dragTargetEvent(event);
+    emit exited(&dragTargetEvent);
+
+    d->containsDrag = false;
+    emit containsDragChanged();
+    if (!d->dragData.isNull()) {
+        d->dragData = QVariant();
+        emit dragDataChanged();
+    }
+}
+
+/*!
+    \qmlsignal DragTarget::onDropped(DragEvent drag)
+    \qmlattachedsignal DragTarget::onDropped(DragEvent drag)
+
+    This handler is called when a drop event occurs within the bounds of a
+    a DragTarget.
+*/
+
+void QSGDragTarget::dragDropEvent(QSGDragEvent *event)
+{
+    Q_D(QSGDragTarget);
+    if (!d->containsDrag) {
+        event->setAccepted(false);
+        return;
+    }
+
+    event->setDropItem(d->dropItem);
+
+    QSGDragTargetEvent dragTargetEvent(event);
+    emit dropped(&dragTargetEvent);
+
+    d->containsDrag = false;
+    emit containsDragChanged();
+    if (!d->dragData.isNull()) {
+        d->dragData = QVariant();
+        emit dragDataChanged();
+    }
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/items/qsgdragtarget_p.h b/src/declarative/items/qsgdragtarget_p.h
new file mode 100644 (file)
index 0000000..ad13e11
--- /dev/null
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGDRAGTARGET_P_H
+#define QSGDRAGTARGET_P_H
+
+#include "qsgitem.h"
+#include "qsgevent.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGDragTargetEvent : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(qreal x READ x)
+    Q_PROPERTY(qreal y READ y)
+    Q_PROPERTY(QVariant data READ data)
+    Q_PROPERTY(QStringList keys READ keys)
+    Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
+public:
+    QSGDragTargetEvent(QSGDragEvent *event) : _event(event) {}
+
+    qreal x() const { return _event->x(); }
+    qreal y() const { return _event->y(); }
+
+    QVariant data() const { return _event->data(); }
+    QStringList keys() const { return _event->keys(); }
+
+    bool accepted() const { return _event->isAccepted(); }
+    void setAccepted(bool accepted) { _event->setAccepted(accepted); }
+
+private:
+    QSGDragEvent *_event;
+};
+
+class QSGDragTargetPrivate;
+class Q_AUTOTEST_EXPORT QSGDragTarget : public QSGItem
+{
+    Q_OBJECT
+    Q_PROPERTY(bool containsDrag READ containsDrag NOTIFY containsDragChanged)
+    Q_PROPERTY(QSGItem *dropItem READ dropItem WRITE setDropItem NOTIFY dropItemChanged RESET resetDropItem)
+    Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
+    Q_PROPERTY(qreal dragX READ dragX NOTIFY dragPositionChanged)
+    Q_PROPERTY(qreal dragY READ dragY NOTIFY dragPositionChanged)
+    Q_PROPERTY(QVariant dragData READ dragData NOTIFY dragDataChanged)
+
+public:
+    QSGDragTarget(QSGItem *parent=0);
+    ~QSGDragTarget();
+
+    bool containsDrag() const;
+    void setContainsDrag(bool drag);
+
+    QStringList keys() const;
+    void setKeys(const QStringList &keys);
+
+    QSGItem *dropItem() const;
+    void setDropItem(QSGItem *item);
+    void resetDropItem();
+
+    qreal dragX() const;
+    qreal dragY() const;
+    QVariant dragData() const;
+
+Q_SIGNALS:
+    void containsDragChanged();
+    void keysChanged();
+    void dropItemChanged();
+    void dragPositionChanged();
+    void dragDataChanged();
+
+    void entered(QSGDragTargetEvent *drag);
+    void exited(QSGDragTargetEvent *drag);
+    void positionChanged(QSGDragTargetEvent *drag);
+    void dropped(QSGDragTargetEvent *drag);
+
+protected:
+    void dragMoveEvent(QSGDragEvent *event);
+    void dragEnterEvent(QSGDragEvent *event);
+    void dragExitEvent(QSGDragEvent *event);
+    void dragDropEvent(QSGDragEvent *event);
+
+private:
+    Q_DISABLE_COPY(QSGDragTarget)
+    Q_DECLARE_PRIVATE(QSGDragTarget)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGDragTargetEvent)
+QML_DECLARE_TYPE(QSGDragTarget)
+
+QT_END_HEADER
+
+#endif // QSGDRAGTARGET_P_H
diff --git a/src/declarative/items/qsgevent.h b/src/declarative/items/qsgevent.h
new file mode 100644 (file)
index 0000000..93b53d1
--- /dev/null
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAGEVENT_H
+#define QDRAGEVENT_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItem;
+
+class Q_DECLARATIVE_EXPORT QSGEvent : public QEvent
+{
+public:
+    // XXX: Merge types into QEvent or formally reserve a suitable range.
+    // Alternatively start from QEvent::User and add a SGUser value for use by items.
+    enum SGType
+    {
+        SGDragEnter = 600,
+        SGDragExit,
+        SGDragMove,
+        SGDragDrop
+    };
+
+    QSGEvent(QSGEvent::SGType type) : QEvent(Type(type)) {}
+
+    SGType type() const { return SGType(QEvent::type()); }
+};
+
+class Q_DECLARATIVE_EXPORT QSGDragEvent : public QSGEvent
+{
+public:
+    QSGDragEvent(
+            SGType type,
+            const QPointF &scenePosition,
+            const QVariant &data,
+            const QStringList &keys,
+            QSGItem *grabItem = 0)
+        : QSGEvent(type)
+        , _scenePosition(scenePosition),
+          _data(data)
+        , _keys(keys)
+        , _dropItem(0)
+        , _grabItem(grabItem)
+    {
+    }
+    QSGDragEvent(SGType type, const QSGDragEvent &event)
+        : QSGEvent(type)
+        , _scenePosition(event._scenePosition)
+        , _position(event._position)
+        , _data(event._data)
+        , _keys(event._keys)
+        , _dropItem(event._dropItem)
+        , _grabItem(event._grabItem)
+    {
+    }
+
+    QVariant data() const { return _data; }
+
+    qreal x() const { return _position.x(); }
+    qreal y() const { return _position.y(); }
+    QPointF position() const { return _position; }
+    void setPosition(const QPointF &position) { _position = position; }
+
+    QPointF scenePosition() const { return _scenePosition; }
+
+    QStringList keys() const { return _keys; }
+
+    QSGItem *dropItem() const { return _dropItem; }
+    void setDropItem(QSGItem *dropItem) { _dropItem = dropItem; }
+
+    QSGItem *grabItem() const { return _grabItem; }
+    void setGrabItem(QSGItem *item) { _grabItem = item; }
+
+private:
+    QPointF _scenePosition;
+    QPointF _position;
+    QVariant _data;
+    QStringList _keys;
+    QSGItem *_dropItem;
+    QSGItem *_grabItem;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
index f2d2695..328da16 100644 (file)
@@ -45,6 +45,7 @@
 #include "qsgcanvas.h"
 #include <QtScript/qscriptengine.h>
 #include "qsgcanvas_p.h"
+#include "qsgevent.h"
 
 #include "qsgevents_p_p.h"
 
@@ -1689,6 +1690,26 @@ void QSGItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
     Q_UNUSED(event);
 }
 
+void QSGItem::dragMoveEvent(QSGDragEvent *event)
+{
+    event->setAccepted(false);
+}
+
+void QSGItem::dragEnterEvent(QSGDragEvent *event)
+{
+    event->setAccepted(false);
+}
+
+void QSGItem::dragExitEvent(QSGDragEvent *event)
+{
+    event->setAccepted(false);
+}
+
+void QSGItem::dragDropEvent(QSGDragEvent *event)
+{
+    event->setAccepted(false);
+}
+
 bool QSGItem::childMouseEventFilter(QSGItem *, QEvent *)
 {
     return false;
@@ -2151,6 +2172,27 @@ void QSGItemPrivate::deliverHoverEvent(QGraphicsSceneHoverEvent *e)
     }
 }
 
+void QSGItemPrivate::deliverDragEvent(QSGDragEvent *e)
+{
+    Q_Q(QSGItem);
+    switch (e->type()) {
+    default:
+        Q_ASSERT(!"Unknown event type");
+    case QSGEvent::SGDragEnter:
+        q->dragEnterEvent(e);
+        break;
+    case QSGEvent::SGDragExit:
+        q->dragExitEvent(e);
+        break;
+    case QSGEvent::SGDragMove:
+        q->dragMoveEvent(e);
+        break;
+    case QSGEvent::SGDragDrop:
+        q->dragDropEvent(e);
+        break;
+    }
+}
+
 void QSGItem::itemChange(ItemChange change, const ItemChangeData &value)
 {
     Q_UNUSED(change);
index 564d819..995b5cb 100644 (file)
@@ -89,6 +89,7 @@ class QSGKeyEvent;
 class QSGAnchors;
 class QSGItemPrivate;
 class QSGCanvas;
+class QSGDragEvent;
 class QSGEngine;
 class QTouchEvent;
 class QSGNode;
@@ -363,6 +364,10 @@ protected:
     virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
     virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
     virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+    virtual void dragMoveEvent(QSGDragEvent *event);
+    virtual void dragEnterEvent(QSGDragEvent *event);
+    virtual void dragExitEvent(QSGDragEvent *event);
+    virtual void dragDropEvent(QSGDragEvent *event);
     virtual bool childMouseEventFilter(QSGItem *, QEvent *);
 
     virtual void geometryChanged(const QRectF &newGeometry,
index 0f4a321..300ccdc 100644 (file)
@@ -324,6 +324,7 @@ public:
     void deliverWheelEvent(QGraphicsSceneWheelEvent *);
     void deliverTouchEvent(QTouchEvent *);
     void deliverHoverEvent(QGraphicsSceneHoverEvent *);
+    void deliverDragEvent(QSGDragEvent *);
 
     bool calcEffectiveVisible() const;
     void setEffectiveVisibleRecur(bool);
index a29776f..f1e3a0c 100644 (file)
@@ -77,6 +77,7 @@
 #include "qsgcontext2d_p.h"
 #include "qsgsprite_p.h"
 #include "qsgspriteimage_p.h"
+#include "qsgdragtarget_p.h"
 
 static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent)
 {
@@ -189,6 +190,9 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
     qmlRegisterType<QSGAnchorSet>();
     qmlRegisterType<QSGAnchorAnimation>(uri, major, minor,"AnchorAnimation");
     qmlRegisterType<QSGParentAnimation>(uri, major, minor,"ParentAnimation");
+
+    qmlRegisterType<QSGDragTarget>("QtQuick", 2, 0, "DragTarget");
+    qmlRegisterType<QSGDragTargetEvent>();
 }
 
 void QSGItemsModule::defineModule()
index 887d78a..6b4311e 100644 (file)
@@ -43,6 +43,7 @@
 #include "qsgmousearea_p.h"
 #include "qsgmousearea_p_p.h"
 #include "qsgcanvas.h"
+#include "qsgevent.h"
 #include "qsgevents_p_p.h"
 
 #include <QtGui/qgraphicssceneevent.h>
@@ -54,8 +55,8 @@ QT_BEGIN_NAMESPACE
 static const int PressAndHoldDelay = 800;
 
 QSGDrag::QSGDrag(QObject *parent)
-: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX),
-_active(false), _filterChildren(false)
+: QObject(parent), _target(0), _dropItem(0), _grabItem(0), _axis(XandYAxis), _xmin(-FLT_MAX),
+_xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false)
 {
 }
 
@@ -78,12 +79,70 @@ void QSGDrag::setTarget(QSGItem *t)
 
 void QSGDrag::resetTarget()
 {
-    if (!_target)
+    if (_target == 0)
         return;
     _target = 0;
     emit targetChanged();
 }
 
+/*!
+    \qmlproperty Item MouseArea::drag.dropItem
+
+    This property holds the item an active drag will be dropped on if released
+    at the current position.
+*/
+
+QSGItem *QSGDrag::dropItem() const
+{
+    return _dropItem;
+}
+
+void QSGDrag::setDropItem(QSGItem *item)
+{
+    if (_dropItem != item) {
+        _dropItem = item;
+        emit dropItemChanged();
+    }
+}
+
+QSGItem *QSGDrag::grabItem() const
+{
+    return _grabItem;
+}
+
+void QSGDrag::setGrabItem(QSGItem *item)
+{
+    _grabItem = item;
+}
+
+/*!
+    \qmlproperty variant MouseArea::drag.data
+
+    This property holds the data sent to recipients of drag events generated
+    by a MouseArea.
+*/
+
+QVariant QSGDrag::data() const
+{
+    return _data;
+}
+
+void QSGDrag::setData(const QVariant &data)
+{
+    if (_data != data) {
+        _data = data;
+        emit dataChanged();
+    }
+}
+
+void QSGDrag::resetData()
+{
+    if (!_data.isNull()) {
+        _data = QVariant();
+        emit dataChanged();
+    }
+}
+
 QSGDrag::Axis QSGDrag::axis() const
 {
     return _axis;
@@ -175,9 +234,30 @@ void QSGDrag::setFilterChildren(bool filter)
     emit filterChildrenChanged();
 }
 
+/*!
+    \qmlproperty stringlist MouseArea::drag.keys
+
+    This property holds a list of keys drag recipients can use to identify the
+    source or data type of a drag event.
+*/
+
+QStringList QSGDrag::keys() const
+{
+    return _keys;
+}
+
+void QSGDrag::setKeys(const QStringList &keys)
+{
+    if (_keys != keys) {
+        _keys = keys;
+        emit keysChanged();
+    }
+}
+
 QSGMouseAreaPrivate::QSGMouseAreaPrivate()
 : absorb(true), hovered(false), pressed(false), longPress(false),
-  moved(false), stealMouse(false), doubleClick(false), preventStealing(false), drag(0)
+  moved(false), stealMouse(false), doubleClick(false), preventStealing(false), dragRejected(false),
+  drag(0)
 {
     Q_Q(QSGMouseArea);
     forwardTo = QDeclarativeListProperty<QSGItem>(q, forwardToList);
@@ -384,6 +464,7 @@ void QSGMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
         QSGItem::mousePressEvent(event);
     else {
         d->longPress = false;
+        d->dragRejected = false;
         d->saveEvent(event);
         if (d->drag) {
             d->dragX = drag()->axis() & QSGDrag::XAxis;
@@ -440,8 +521,24 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
         qreal dx = qAbs(curLocalPos.x() - startLocalPos.x());
         qreal dy = qAbs(curLocalPos.y() - startLocalPos.y());
 
-        if (keepMouseGrab() && d->stealMouse)
-            d->drag->setActive(true);
+        if (keepMouseGrab() && d->stealMouse && !d->dragRejected && !d->drag->active()) {
+            QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+            d->drag->emitDragged(&me);
+            if (me.isAccepted()) {
+                d->drag->setActive(true);
+                QSGDragEvent dragEvent(
+                        QSGEvent::SGDragEnter,
+                        d->startScene,
+                        d->drag->data(),
+                        d->drag->keys());
+                QCoreApplication::sendEvent(canvas(), &dragEvent);
+
+                d->drag->setGrabItem(dragEvent.grabItem());
+                d->drag->setDropItem(dragEvent.dropItem());
+            } else {
+                d->dragRejected = true;
+            }
+        }
 
         if (d->dragX && d->drag->active()) {
             qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX;
@@ -470,6 +567,18 @@ void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
         }
 
         d->moved = true;
+
+        if (d->drag->active()) {
+            QSGDragEvent dragEvent(
+                    QSGEvent::SGDragMove,
+                    event->scenePos(),
+                    d->drag->data(),
+                    d->drag->keys(),
+                    d->drag->grabItem());
+            QCoreApplication::sendEvent(canvas(), &dragEvent);
+            d->drag->setGrabItem(dragEvent.grabItem());
+            d->drag->setDropItem(dragEvent.dropItem());
+        }
     }
     QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
     emit mousePositionChanged(&me);
@@ -490,8 +599,24 @@ void QSGMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
     } else {
         d->saveEvent(event);
         setPressed(false);
-        if (d->drag)
+        if (d->drag && d->drag->active()) {
+            QSGDragEvent dragEvent(
+                    QSGEvent::SGDragDrop,
+                    event->scenePos(),
+                    d->drag->data(),
+                    d->drag->keys(),
+                    d->drag->grabItem());
+            QCoreApplication::sendEvent(canvas(), &dragEvent);
+            d->drag->setGrabItem(0);
+            if (dragEvent.isAccepted()) {
+                d->drag->setDropItem(dragEvent.dropItem());
+                d->drag->emitDropped(dragEvent.dropItem());
+            } else {
+                d->drag->emitCanceled();
+            }
+            d->drag->setDropItem(0);
             d->drag->setActive(false);
+        }
         // If we don't accept hover, we need to reset containsMouse.
         if (!acceptHoverEvents())
             setHovered(false);
index 469b9f7..d7248bc 100644 (file)
@@ -51,12 +51,15 @@ QT_BEGIN_NAMESPACE
 
 QT_MODULE(Declarative)
 
+class QSGMouseEvent;
 class Q_AUTOTEST_EXPORT QSGDrag : public QObject
 {
     Q_OBJECT
 
     Q_ENUMS(Axis)
     Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget)
+    Q_PROPERTY(QSGItem *dropItem READ dropItem NOTIFY dropItemChanged)
+    Q_PROPERTY(QVariant data READ data WRITE setData NOTIFY dataChanged RESET resetData)
     Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged)
     Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged)
     Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged)
@@ -64,6 +67,7 @@ class Q_AUTOTEST_EXPORT QSGDrag : public QObject
     Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
     Q_PROPERTY(bool active READ active NOTIFY activeChanged)
     Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged)
+    Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
     //### consider drag and drop
 
 public:
@@ -74,6 +78,16 @@ public:
     void setTarget(QSGItem *);
     void resetTarget();
 
+    QSGItem *dropItem() const;
+    void setDropItem(QSGItem *item);
+
+    QSGItem *grabItem() const;
+    void setGrabItem(QSGItem *grabItem);
+
+    QVariant data() const;
+    void setData(const QVariant &data);
+    void resetData();
+
     enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 };
     Axis axis() const;
     void setAxis(Axis);
@@ -93,8 +107,17 @@ public:
     bool filterChildren() const;
     void setFilterChildren(bool);
 
+    QStringList keys() const;
+    void setKeys(const QStringList &keys);
+
+    void emitDragged(QSGMouseEvent *event) { emit dragged(event); }
+    void emitDropped(QSGItem *dropItem) { emit dropped(dropItem); }
+    void emitCanceled() { emit canceled(); }
+
 Q_SIGNALS:
     void targetChanged();
+    void dropItemChanged();
+    void dataChanged();
     void axisChanged();
     void minimumXChanged();
     void maximumXChanged();
@@ -102,9 +125,17 @@ Q_SIGNALS:
     void maximumYChanged();
     void activeChanged();
     void filterChildrenChanged();
+    void keysChanged();
+    void dragged(QSGMouseEvent *mouse);
+    void dropped(QSGItem *dropItem);
+    void canceled();
 
 private:
+    QStringList _keys;
+    QVariant _data;
     QSGItem *_target;
+    QSGItem *_dropItem;
+    QSGItem *_grabItem;
     Axis _axis;
     qreal _xmin;
     qreal _xmax;
@@ -115,7 +146,6 @@ private:
     Q_DISABLE_COPY(QSGDrag)
 };
 
-class QSGMouseEvent;
 class QSGMouseAreaPrivate;
 class Q_AUTOTEST_EXPORT QSGMouseArea : public QSGItem
 {
index e736c05..11f7089 100644 (file)
@@ -96,6 +96,7 @@ public:
     bool stealMouse : 1;
     bool doubleClick : 1;
     bool preventStealing : 1;
+    bool dragRejected : 1;
     QSGDrag *drag;
     QPointF startScene;
     qreal startX;