From 73081387d84c4b77dafeda927d1fc1ebee6cfdf2 Mon Sep 17 00:00:00 2001 From: Andrew den Exter Date: Thu, 9 Jun 2011 18:29:50 +1000 Subject: [PATCH] Add a DragTarget element. Provides an area that can be used to handle events when other items are dragged over it. Task-number: QMLNG-32 --- .../declarative/dragtarget/dragtarget.qmlproject | 16 + .../declarative/dragtarget/lists/listmodel.qml | 256 ++++++++++++++ .../declarative/dragtarget/lists/lists.qmlproject | 16 + examples/declarative/dragtarget/text/dragtext.qml | 142 ++++++++ .../declarative/dragtarget/text/text.qmlproject | 16 + examples/declarative/dragtarget/tiles/DragTile.qml | 59 ++++ examples/declarative/dragtarget/tiles/DropTile.qml | 30 ++ examples/declarative/dragtarget/tiles/tiles.qml | 85 +++++ src/declarative/items/items.pri | 3 + src/declarative/items/qsgcanvas.cpp | 86 +++++ src/declarative/items/qsgcanvas_p.h | 3 + src/declarative/items/qsgdragtarget.cpp | 361 ++++++++++++++++++++ src/declarative/items/qsgdragtarget_p.h | 137 ++++++++ src/declarative/items/qsgevent.h | 137 ++++++++ src/declarative/items/qsgitem.cpp | 42 +++ src/declarative/items/qsgitem.h | 5 + src/declarative/items/qsgitem_p.h | 1 + src/declarative/items/qsgitemsmodule.cpp | 4 + src/declarative/items/qsgmousearea.cpp | 139 +++++++- src/declarative/items/qsgmousearea_p.h | 32 ++- src/declarative/items/qsgmousearea_p_p.h | 1 + 21 files changed, 1563 insertions(+), 8 deletions(-) create mode 100644 examples/declarative/dragtarget/dragtarget.qmlproject create mode 100644 examples/declarative/dragtarget/lists/listmodel.qml create mode 100644 examples/declarative/dragtarget/lists/lists.qmlproject create mode 100644 examples/declarative/dragtarget/text/dragtext.qml create mode 100644 examples/declarative/dragtarget/text/text.qmlproject create mode 100644 examples/declarative/dragtarget/tiles/DragTile.qml create mode 100644 examples/declarative/dragtarget/tiles/DropTile.qml create mode 100644 examples/declarative/dragtarget/tiles/tiles.qml create mode 100644 src/declarative/items/qsgdragtarget.cpp create mode 100644 src/declarative/items/qsgdragtarget_p.h create mode 100644 src/declarative/items/qsgevent.h diff --git a/examples/declarative/dragtarget/dragtarget.qmlproject b/examples/declarative/dragtarget/dragtarget.qmlproject new file mode 100644 index 0000000..d4909f8 --- /dev/null +++ b/examples/declarative/dragtarget/dragtarget.qmlproject @@ -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 index 0000000..50b1d39 --- /dev/null +++ b/examples/declarative/dragtarget/lists/listmodel.qml @@ -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 index 0000000..d4909f8 --- /dev/null +++ b/examples/declarative/dragtarget/lists/lists.qmlproject @@ -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 index 0000000..c4a4f24 --- /dev/null +++ b/examples/declarative/dragtarget/text/dragtext.qml @@ -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 index 0000000..d4909f8 --- /dev/null +++ b/examples/declarative/dragtarget/text/text.qmlproject @@ -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 index 0000000..213373a --- /dev/null +++ b/examples/declarative/dragtarget/tiles/DragTile.qml @@ -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 index 0000000..9d96875 --- /dev/null +++ b/examples/declarative/dragtarget/tiles/DropTile.qml @@ -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 index 0000000..d8bcb39 --- /dev/null +++ b/examples/declarative/dragtarget/tiles/tiles.qml @@ -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" + } + } + } +} diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri index f29a82e..bf92025 100644 --- a/src/declarative/items/items.pri +++ b/src/declarative/items/items.pri @@ -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 \ diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp index f991609..3a88fbb 100644 --- a/src/declarative/items/qsgcanvas.cpp +++ b/src/declarative/items/qsgcanvas.cpp @@ -45,6 +45,8 @@ #include "qsgitem.h" #include "qsgitem_p.h" +#include "qsgevent.h" + #include #include @@ -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(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 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(e)); break; + case QSGEvent::SGDragEnter: + case QSGEvent::SGDragExit: + case QSGEvent::SGDragMove: + case QSGEvent::SGDragDrop: + QSGItemPrivate::get(item)->deliverDragEvent(static_cast(e)); + break; default: break; } diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h index 90132f8..9b2683c 100644 --- a/src/declarative/items/qsgcanvas_p.h +++ b/src/declarative/items/qsgcanvas_p.h @@ -55,6 +55,7 @@ #include "qsgitem.h" #include "qsgcanvas.h" +#include "qsgevent.h" #include #include @@ -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 hoverItem; enum FocusOption { diff --git a/src/declarative/items/qsgdragtarget.cpp b/src/declarative/items/qsgdragtarget.cpp new file mode 100644 index 0000000..c1ed167 --- /dev/null +++ b/src/declarative/items/qsgdragtarget.cpp @@ -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 index 0000000..ad13e11 --- /dev/null +++ b/src/declarative/items/qsgdragtarget_p.h @@ -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 index 0000000..93b53d1 --- /dev/null +++ b/src/declarative/items/qsgevent.h @@ -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 +#include +#include +#include +#include + +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 + diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp index f2d2695..328da16 100644 --- a/src/declarative/items/qsgitem.cpp +++ b/src/declarative/items/qsgitem.cpp @@ -45,6 +45,7 @@ #include "qsgcanvas.h" #include #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); diff --git a/src/declarative/items/qsgitem.h b/src/declarative/items/qsgitem.h index 564d819..995b5cb 100644 --- a/src/declarative/items/qsgitem.h +++ b/src/declarative/items/qsgitem.h @@ -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, diff --git a/src/declarative/items/qsgitem_p.h b/src/declarative/items/qsgitem_p.h index 0f4a321..300ccdc 100644 --- a/src/declarative/items/qsgitem_p.h +++ b/src/declarative/items/qsgitem_p.h @@ -324,6 +324,7 @@ public: void deliverWheelEvent(QGraphicsSceneWheelEvent *); void deliverTouchEvent(QTouchEvent *); void deliverHoverEvent(QGraphicsSceneHoverEvent *); + void deliverDragEvent(QSGDragEvent *); bool calcEffectiveVisible() const; void setEffectiveVisibleRecur(bool); diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp index a29776f..f1e3a0c 100644 --- a/src/declarative/items/qsgitemsmodule.cpp +++ b/src/declarative/items/qsgitemsmodule.cpp @@ -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(); qmlRegisterType(uri, major, minor,"AnchorAnimation"); qmlRegisterType(uri, major, minor,"ParentAnimation"); + + qmlRegisterType("QtQuick", 2, 0, "DragTarget"); + qmlRegisterType(); } void QSGItemsModule::defineModule() diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp index 887d78a..6b4311e 100644 --- a/src/declarative/items/qsgmousearea.cpp +++ b/src/declarative/items/qsgmousearea.cpp @@ -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 @@ -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(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); diff --git a/src/declarative/items/qsgmousearea_p.h b/src/declarative/items/qsgmousearea_p.h index 469b9f7..d7248bc 100644 --- a/src/declarative/items/qsgmousearea_p.h +++ b/src/declarative/items/qsgmousearea_p.h @@ -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 { diff --git a/src/declarative/items/qsgmousearea_p_p.h b/src/declarative/items/qsgmousearea_p_p.h index e736c05..11f7089 100644 --- a/src/declarative/items/qsgmousearea_p_p.h +++ b/src/declarative/items/qsgmousearea_p_p.h @@ -96,6 +96,7 @@ public: bool stealMouse : 1; bool doubleClick : 1; bool preventStealing : 1; + bool dragRejected : 1; QSGDrag *drag; QPointF startScene; qreal startX; -- 1.7.2.5