aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/maskedmousearea/images/cloud_1.pngbin0 -> 49524 bytes
-rw-r--r--examples/quick/maskedmousearea/images/cloud_2.pngbin0 -> 32288 bytes
-rw-r--r--examples/quick/maskedmousearea/images/moon.pngbin0 -> 13263 bytes
-rw-r--r--examples/quick/maskedmousearea/main.cpp57
-rw-r--r--examples/quick/maskedmousearea/maskedmousearea.cpp141
-rw-r--r--examples/quick/maskedmousearea/maskedmousearea.h98
-rw-r--r--examples/quick/maskedmousearea/maskedmousearea.pro14
-rw-r--r--examples/quick/maskedmousearea/maskedmousearea.qml135
-rw-r--r--examples/quick/maskedmousearea/maskedmousearea.qmlproject16
-rw-r--r--src/quick/items/qquickcanvas.cpp23
-rw-r--r--src/quick/items/qquickflickable.cpp6
-rw-r--r--src/quick/items/qquickitem.cpp22
-rw-r--r--src/quick/items/qquickitem.h2
-rw-r--r--src/quick/items/qquickmousearea.cpp16
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp18
-rw-r--r--src/quick/items/qquickpathview.cpp13
-rw-r--r--src/quick/items/qquickpincharea.cpp6
-rw-r--r--tests/auto/quick/qquickitem2/data/hollowTestItem.qml38
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp171
19 files changed, 730 insertions, 46 deletions
diff --git a/examples/quick/maskedmousearea/images/cloud_1.png b/examples/quick/maskedmousearea/images/cloud_1.png
new file mode 100644
index 0000000000..87c54af253
--- /dev/null
+++ b/examples/quick/maskedmousearea/images/cloud_1.png
Binary files differ
diff --git a/examples/quick/maskedmousearea/images/cloud_2.png b/examples/quick/maskedmousearea/images/cloud_2.png
new file mode 100644
index 0000000000..981bbd2630
--- /dev/null
+++ b/examples/quick/maskedmousearea/images/cloud_2.png
Binary files differ
diff --git a/examples/quick/maskedmousearea/images/moon.png b/examples/quick/maskedmousearea/images/moon.png
new file mode 100644
index 0000000000..0a8037dd85
--- /dev/null
+++ b/examples/quick/maskedmousearea/images/moon.png
Binary files differ
diff --git a/examples/quick/maskedmousearea/main.cpp b/examples/quick/maskedmousearea/main.cpp
new file mode 100644
index 0000000000..407497e640
--- /dev/null
+++ b/examples/quick/maskedmousearea/main.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQuickView>
+
+#include "maskedmousearea.h"
+
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc,argv);
+ QQuickView view;
+
+ qmlRegisterType<MaskedMouseArea>("Example", 1, 0, "MaskedMouseArea");
+
+ view.setSource(QUrl::fromLocalFile("maskedmousearea.qml"));
+ view.show();
+ return app.exec();
+}
diff --git a/examples/quick/maskedmousearea/maskedmousearea.cpp b/examples/quick/maskedmousearea/maskedmousearea.cpp
new file mode 100644
index 0000000000..3f872a66f6
--- /dev/null
+++ b/examples/quick/maskedmousearea/maskedmousearea.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "maskedmousearea.h"
+
+#include <QStyleHints>
+#include <QGuiApplication>
+
+
+MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
+ : QQuickItem(parent),
+ m_pressed(false),
+ m_alphaThreshold(0.0),
+ m_containsMouse(false)
+{
+ setAcceptHoverEvents(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+void MaskedMouseArea::setPressed(bool pressed)
+{
+ if (m_pressed != pressed) {
+ m_pressed = pressed;
+ emit pressedChanged();
+ }
+}
+
+void MaskedMouseArea::setContainsMouse(bool containsMouse)
+{
+ if (m_containsMouse != containsMouse) {
+ m_containsMouse = containsMouse;
+ emit containsMouseChanged();
+ }
+}
+
+void MaskedMouseArea::setMaskSource(const QUrl &source)
+{
+ if (m_maskSource != source) {
+ m_maskSource = source;
+ m_maskImage = QImage(source.toLocalFile());
+ emit maskSourceChanged();
+ }
+}
+
+void MaskedMouseArea::setAlphaThreshold(qreal threshold)
+{
+ if (m_alphaThreshold != threshold) {
+ m_alphaThreshold = threshold;
+ emit alphaThresholdChanged();
+ }
+}
+
+bool MaskedMouseArea::contains(const QPointF &point) const
+{
+ if (!QQuickItem::contains(point) || m_maskImage.isNull())
+ return false;
+
+ QPoint p = point.toPoint();
+
+ if (p.x() < 0 || p.x() >= m_maskImage.width() ||
+ p.y() < 0 || p.y() >= m_maskImage.height())
+ return false;
+
+ qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
+ return qAlpha(m_maskImage.pixel(p)) > r;
+}
+
+void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
+{
+ setPressed(true);
+ m_pressPoint = event->pos();
+ emit pressed();
+}
+
+void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
+{
+ setPressed(false);
+ emit released();
+
+ const int threshold = qApp->styleHints()->startDragDistance();
+ const bool isClick = (threshold >= qAbs(event->x() - m_pressPoint.x()) &&
+ threshold >= qAbs(event->y() - m_pressPoint.y()));
+
+ if (isClick)
+ emit clicked();
+}
+
+void MaskedMouseArea::mouseUngrabEvent()
+{
+ setPressed(false);
+ emit canceled();
+}
+
+void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ setContainsMouse(true);
+}
+
+void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
+{
+ Q_UNUSED(event);
+ setContainsMouse(false);
+}
diff --git a/examples/quick/maskedmousearea/maskedmousearea.h b/examples/quick/maskedmousearea/maskedmousearea.h
new file mode 100644
index 0000000000..045d02d6f0
--- /dev/null
+++ b/examples/quick/maskedmousearea/maskedmousearea.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MASKEDMOUSEAREA_H
+#define MASKEDMOUSEAREA_H
+
+#include <QImage>
+#include <QQuickItem>
+
+
+class MaskedMouseArea : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
+ Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
+ Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged)
+ Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged)
+
+public:
+ MaskedMouseArea(QQuickItem *parent = 0);
+
+ bool contains(const QPointF &point) const;
+
+ bool isPressed() const { return m_pressed; }
+ bool containsMouse() const { return m_containsMouse; }
+
+ QUrl maskSource() const { return m_maskSource; }
+ void setMaskSource(const QUrl &source);
+
+ qreal alphaThreshold() const { return m_alphaThreshold; }
+ void setAlphaThreshold(qreal threshold);
+
+signals:
+ void pressed();
+ void released();
+ void clicked();
+ void canceled();
+ void pressedChanged();
+ void maskSourceChanged();
+ void containsMouseChanged();
+ void alphaThresholdChanged();
+
+protected:
+ void setPressed(bool pressed);
+ void setContainsMouse(bool containsMouse);
+ void mousePressEvent(QMouseEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void hoverEnterEvent(QHoverEvent *event);
+ void hoverLeaveEvent(QHoverEvent *event);
+ void mouseUngrabEvent();
+
+private:
+ bool m_pressed;
+ QUrl m_maskSource;
+ QImage m_maskImage;
+ QPointF m_pressPoint;
+ qreal m_alphaThreshold;
+ bool m_containsMouse;
+};
+
+#endif
diff --git a/examples/quick/maskedmousearea/maskedmousearea.pro b/examples/quick/maskedmousearea/maskedmousearea.pro
new file mode 100644
index 0000000000..53e14aaa4b
--- /dev/null
+++ b/examples/quick/maskedmousearea/maskedmousearea.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += quick qml
+
+HEADERS += maskedmousearea.h
+
+SOURCES += main.cpp \
+ maskedmousearea.cpp
+
+target.path = $$[QT_INSTALL_EXAMPLES]/qtdeclarative/qtquick/maskedmousearea
+qml.files = maskedmousearea.qml images
+qml.path = $$[QT_INSTALL_EXAMPLES]/qtdeclarative/qtquick/maskedmousearea
+INSTALLS += target qml
+
diff --git a/examples/quick/maskedmousearea/maskedmousearea.qml b/examples/quick/maskedmousearea/maskedmousearea.qml
new file mode 100644
index 0000000000..82d351b04f
--- /dev/null
+++ b/examples/quick/maskedmousearea/maskedmousearea.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Example 1.0
+
+Rectangle {
+ height: 480
+ width: 320
+ color: "black"
+
+ Text {
+ text: qsTr("CLICK AND HOVER")
+ opacity: 0.6
+ color: "white"
+ font.pixelSize: 20
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 50
+ }
+
+ Image {
+ id: moon
+ smooth: true
+ anchors.centerIn: parent
+ scale: moonArea.pressed ? 1.1 : 1.0
+ opacity: moonArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/moon.png")
+
+ MaskedMouseArea {
+ id: moonArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: moon.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+
+ Image {
+ id: rightCloud
+ anchors {
+ centerIn: moon
+ verticalCenterOffset: 30
+ horizontalCenterOffset: 80
+ }
+ smooth: true
+ scale: rightCloudArea.pressed ? 1.1 : 1.0
+ opacity: rightCloudArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/cloud_2.png")
+
+ MaskedMouseArea {
+ id: rightCloudArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: rightCloud.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+
+ Image {
+ id: leftCloud
+ anchors {
+ centerIn: moon
+ verticalCenterOffset: 40
+ horizontalCenterOffset: -80
+ }
+ smooth: true
+ scale: leftCloudArea.pressed ? 1.1 : 1.0
+ opacity: leftCloudArea.containsMouse ? 1.0 : 0.7
+ source: Qt.resolvedUrl("images/cloud_1.png")
+
+ MaskedMouseArea {
+ id: leftCloudArea
+ anchors.fill: parent
+ alphaThreshold: 0.4
+ maskSource: leftCloud.source
+ }
+
+ Behavior on opacity {
+ NumberAnimation { duration: 200 }
+ }
+ Behavior on scale {
+ NumberAnimation { duration: 100 }
+ }
+ }
+}
diff --git a/examples/quick/maskedmousearea/maskedmousearea.qmlproject b/examples/quick/maskedmousearea/maskedmousearea.qmlproject
new file mode 100644
index 0000000000..709c19866f
--- /dev/null
+++ b/examples/quick/maskedmousearea/maskedmousearea.qmlproject
@@ -0,0 +1,16 @@
+import QmlProject 1.1
+
+Project {
+ mainFile: "maskedmousearea.qml"
+
+ /* Include .qml, .js, and image files from current directory and subdirectories */
+ QmlFiles {
+ directory: "."
+ }
+ JavaScriptFiles {
+ directory: "."
+ }
+ ImageFiles {
+ directory: "."
+ }
+}
diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp
index a288531beb..91102331a6 100644
--- a/src/quick/items/qquickcanvas.cpp
+++ b/src/quick/items/qquickcanvas.cpp
@@ -1039,7 +1039,7 @@ bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouse
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
QPointF p = item->mapFromScene(event->windowPos());
- if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ if (!item->contains(p))
return false;
}
@@ -1054,7 +1054,7 @@ bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouse
if (itemPrivate->acceptedMouseButtons() & event->button()) {
QPointF p = item->mapFromScene(event->windowPos());
- if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ if (item->contains(p)) {
QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
me.accept();
@@ -1218,7 +1218,7 @@ bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
QPointF p = item->mapFromScene(scenePos);
- if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ if (!item->contains(p))
return false;
}
@@ -1233,7 +1233,7 @@ bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
- if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ if (item->contains(p)) {
if (!hoverItems.isEmpty() && hoverItems[0] == item) {
//move
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
@@ -1286,7 +1286,7 @@ bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
QPointF p = item->mapFromScene(event->posF());
- if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ if (!item->contains(p))
return false;
}
@@ -1300,7 +1300,8 @@ bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event
}
QPointF p = item->mapFromScene(event->posF());
- if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+
+ if (item->contains(p)) {
QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(),
event->orientation(), event->buttons(), event->modifiers());
wheel.accept();
@@ -1422,10 +1423,9 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
return false;
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QRectF bounds(0, 0, item->width(), item->height());
for (int i=0; i<newPoints.count(); i++) {
QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (!bounds.contains(p))
+ if (!item->contains(p))
return false;
}
}
@@ -1441,12 +1441,11 @@ bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
QList<QTouchEvent::TouchPoint> matchingPoints;
if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
- QRectF bounds(0, 0, item->width(), item->height());
for (int i=0; i<newPoints.count(); i++) {
if (acceptedNewPoints->contains(newPoints[i].id()))
continue;
QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (bounds.contains(p))
+ if (item->contains(p))
matchingPoints << newPoints[i];
}
}
@@ -1537,7 +1536,7 @@ void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
moveEvent->setAccepted(true);
for (++grabItem; grabItem != grabber->end();) {
QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
- if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
+ if ((**grabItem)->contains(p)) {
QDragMoveEvent translatedEvent(
p.toPoint(),
moveEvent->possibleActions(),
@@ -1582,7 +1581,7 @@ bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
return false;
QPointF p = item->mapFromScene(event->pos());
- if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ if (item->contains(p)) {
if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
QDragMoveEvent translatedEvent(
p.toPoint(),
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 75c9919e34..76d36987dd 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1772,14 +1772,14 @@ void QQuickFlickable::mouseUngrabEvent()
bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
{
Q_D(QQuickFlickable);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QPointF localPos = mapFromScene(event->windowPos());
QQuickCanvas *c = canvas();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool disabledItem = grabber && !grabber->isEnabled();
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
- QQuickMouseEventEx mouseEvent(event->type(), mapFromScene(event->windowPos()),
+ if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
+ QQuickMouseEventEx mouseEvent(event->type(), localPos,
event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index ffaec540a2..628e19dbec 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -4886,9 +4886,7 @@ bool QQuickItem::isUnderMouse() const
return false;
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
- if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos))))
- return true;
- return false;
+ return contains(mapFromScene(cursorPos)); // ### refactor: d->canvas->mapFromGlobal(cursorPos))))
}
bool QQuickItem::acceptHoverEvents() const
@@ -5056,6 +5054,24 @@ void QQuickItem::setKeepTouchGrab(bool keep)
}
/*!
+ Returns true if this item contains \a point, which is in local coordinates;
+ returns false otherwise.
+
+ This function can be overwritten in order to handle point collisions in items
+ with custom shapes. The default implementation checks if the point is inside
+ the item's bounding rect.
+
+ Note that it's normally used to check if the item is under the mouse cursor,
+ and for that reason, the implementation of this function should be as light-weight
+ as possible.
+*/
+bool QQuickItem::contains(const QPointF &point) const
+{
+ Q_D(const QQuickItem);
+ return QRectF(0, 0, d->width, d->height).contains(point);
+}
+
+/*!
\qmlmethod object QtQuick2::Item::mapFromItem(Item item, real x, real y)
Maps the point (\a x, \a y), which is in \a item's coordinate system, to
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 16dee80380..0d74cc0c4d 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -292,6 +292,8 @@ public:
bool keepTouchGrab() const;
void setKeepTouchGrab(bool);
+ Q_INVOKABLE virtual bool contains(const QPointF &point) const;
+
QTransform itemTransform(QQuickItem *, bool *) const;
QPointF mapToItem(const QQuickItem *item, const QPointF &point) const;
QPointF mapToScene(const QPointF &point) const;
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index b8555124eb..956ca09aac 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -263,7 +263,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
QPointF p = item->mapFromScene(sp);
- if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ if (!item->contains(p))
return false;
}
@@ -293,7 +293,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
break;
}
QPointF p = item->mapFromScene(sp);
- if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ if (item->contains(p)) {
ev->setX(p.x());
ev->setY(p.y());
ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide
@@ -725,10 +725,10 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
// ### we should skip this if these signals aren't used
// ### can GV handle this for us?
- bool contains = boundingRect().contains(d->lastPos);
- if (d->hovered && !contains)
+ const bool isInside = contains(d->lastPos);
+ if (d->hovered && !isInside)
setHovered(false);
- else if (!d->hovered && contains)
+ else if (!d->hovered && isInside)
setHovered(true);
if (d->drag && d->drag->target()) {
@@ -921,13 +921,13 @@ void QQuickMouseArea::mouseUngrabEvent()
bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
{
Q_D(QQuickMouseArea);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QPointF localPos = mapFromScene(event->windowPos());
QQuickCanvas *c = canvas();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
- QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
+ if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
+ QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
mouseEvent.setAccepted(false);
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 2ba7b80748..df5edfb00d 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -644,13 +644,13 @@ void QQuickMultiPointTouchArea::touchUngrabEvent()
bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event)
{
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QPointF localPos = mapFromScene(event->windowPos());
QQuickCanvas *c = canvas();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool stealThisEvent = _stealMouse;
- if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
- QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
+ if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
+ QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
mouseEvent.setAccepted(false);
@@ -724,25 +724,23 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool disabledItem = grabber && !grabber->isEnabled();
bool stealThisEvent = _stealMouse;
- bool contains = false;
+ bool containsPoint = false;
if (!stealThisEvent) {
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease: {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
- contains = myRect.contains(me->windowPos());
+ containsPoint = contains(mapFromScene(me->windowPos()));
}
break;
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
QTouchEvent *te = static_cast<QTouchEvent*>(event);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) {
- if (myRect.contains(point.scenePos())) {
- contains = true;
+ if (contains(mapFromScene(point.scenePos()))) {
+ containsPoint = true;
break;
}
}
@@ -752,7 +750,7 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
break;
}
}
- if ((stealThisEvent || contains) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
+ if ((stealThisEvent || containsPoint) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
return true;
}
ungrab();
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 836943c478..ba5b237596 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -1300,12 +1300,10 @@ void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event)
if (!interactive || !items.count() || !model || !modelCount)
return;
velocityBuffer.clear();
- QPointF scenePoint = q->mapToScene(event->localPos());
int idx = 0;
for (; idx < items.count(); ++idx) {
- QRectF rect = items.at(idx)->boundingRect();
- rect = items.at(idx)->mapRectToScene(rect);
- if (rect.contains(scenePoint))
+ QQuickItem *item = items.at(idx);
+ if (item->contains(item->mapFromScene(event->windowPos())))
break;
}
if (idx == items.count() && dragMargin == 0.) // didn't click on an item
@@ -1470,12 +1468,13 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
bool QQuickPathView::sendMouseEvent(QMouseEvent *event)
{
Q_D(QQuickPathView);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QPointF localPos = mapFromScene(event->windowPos());
+
QQuickCanvas *c = canvas();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
- QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
+ if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
+ QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
mouseEvent.setAccepted(false);
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index 6f79385d96..eba9d098ff 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -503,13 +503,13 @@ void QQuickPinchArea::mouseUngrabEvent()
bool QQuickPinchArea::sendMouseEvent(QMouseEvent *event)
{
Q_D(QQuickPinchArea);
- QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QPointF localPos = mapFromScene(event->windowPos());
QQuickCanvas *c = canvas();
QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || myRect.contains(event->windowPos())) && (!grabber || !grabber->keepMouseGrab())) {
- QMouseEvent mouseEvent(event->type(), mapFromScene(event->windowPos()), event->windowPos(), event->screenPos(),
+ if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) {
+ QMouseEvent mouseEvent(event->type(), localPos, event->windowPos(), event->screenPos(),
event->button(), event->buttons(), event->modifiers());
mouseEvent.setAccepted(false);
diff --git a/tests/auto/quick/qquickitem2/data/hollowTestItem.qml b/tests/auto/quick/qquickitem2/data/hollowTestItem.qml
new file mode 100644
index 0000000000..d07d89c4f4
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/hollowTestItem.qml
@@ -0,0 +1,38 @@
+import QtQuick 2.0
+import Test 1.0
+
+Rectangle {
+ width: 400
+ height: 400
+
+ Rectangle {
+ x: 100
+ y: 100
+ width: 200
+ height: 200
+ rotation: 45
+
+ Rectangle {
+ scale: 0.5
+ color: "black"
+ anchors.fill: parent
+ radius: hollowItem.circle ? 100 : 0
+
+ Rectangle {
+ color: "white"
+ anchors.centerIn: parent
+ width: hollowItem.holeRadius * 2
+ height: hollowItem.holeRadius * 2
+ radius: hollowItem.circle ? 100 : 0
+ }
+
+ HollowTestItem {
+ id: hollowItem
+ anchors.fill: parent
+ objectName: "hollowItem"
+ holeRadius: 50
+ circle: circleShapeTest
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index c2390c4525..34f51875c9 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -93,6 +93,9 @@ private slots:
void qtbug_16871();
void visibleChildren();
void parentLoop();
+ void contains_data();
+ void contains();
+
private:
QQmlEngine engine;
};
@@ -178,6 +181,69 @@ public:
QML_DECLARE_TYPE(KeyTestItem);
+class HollowTestItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool circle READ isCircle WRITE setCircle)
+ Q_PROPERTY(qreal holeRadius READ holeRadius WRITE setHoleRadius)
+
+public:
+ HollowTestItem(QQuickItem *parent = 0)
+ : QQuickItem(parent),
+ m_isPressed(false),
+ m_isHovered(false),
+ m_isCircle(false),
+ m_holeRadius(50)
+ {
+ setAcceptHoverEvents(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+ }
+
+ bool isPressed() const { return m_isPressed; }
+ bool isHovered() const { return m_isHovered; }
+
+ bool isCircle() const { return m_isCircle; }
+ void setCircle(bool circle) { m_isCircle = circle; }
+
+ qreal holeRadius() const { return m_holeRadius; }
+ void setHoleRadius(qreal radius) { m_holeRadius = radius; }
+
+ bool contains(const QPointF &point) const {
+ const qreal w = width();
+ const qreal h = height();
+ const qreal r = m_holeRadius;
+
+ // check boundaries
+ if (!QRectF(0, 0, w, h).contains(point))
+ return false;
+
+ // square shape
+ if (!m_isCircle)
+ return !QRectF(w / 2 - r, h / 2 - r, r * 2, r * 2).contains(point);
+
+ // circle shape
+ const qreal dx = point.x() - (w / 2);
+ const qreal dy = point.y() - (h / 2);
+ const qreal dd = (dx * dx) + (dy * dy);
+ const qreal outerRadius = qMin<qreal>(w / 2, h / 2);
+ return dd > (r * r) && dd <= outerRadius * outerRadius;
+ }
+
+protected:
+ void hoverEnterEvent(QHoverEvent *) { m_isHovered = true; }
+ void hoverLeaveEvent(QHoverEvent *) { m_isHovered = false; }
+ void mousePressEvent(QMouseEvent *) { m_isPressed = true; }
+ void mouseReleaseEvent(QMouseEvent *) { m_isPressed = false; }
+
+private:
+ bool m_isPressed;
+ bool m_isHovered;
+ bool m_isCircle;
+ qreal m_holeRadius;
+};
+
+QML_DECLARE_TYPE(HollowTestItem);
+
tst_QQuickItem::tst_QQuickItem()
{
@@ -187,6 +253,7 @@ void tst_QQuickItem::initTestCase()
{
QQmlDataTest::initTestCase();
qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem");
+ qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem");
}
void tst_QQuickItem::cleanup()
@@ -1375,6 +1442,110 @@ void tst_QQuickItem::parentLoop()
delete canvas;
}
+void tst_QQuickItem::contains_data()
+{
+ QTest::addColumn<bool>("circleTest");
+ QTest::addColumn<bool>("insideTarget");
+ QTest::addColumn<QList<QPoint> >("points");
+
+ QList<QPoint> points;
+
+ points << QPoint(176, 176)
+ << QPoint(176, 226)
+ << QPoint(226, 176)
+ << QPoint(226, 226)
+ << QPoint(150, 200)
+ << QPoint(200, 150)
+ << QPoint(200, 250)
+ << QPoint(250, 200);
+ QTest::newRow("hollow square: testing points inside") << false << true << points;
+
+ points.clear();
+ points << QPoint(162, 162)
+ << QPoint(162, 242)
+ << QPoint(242, 162)
+ << QPoint(242, 242)
+ << QPoint(200, 200)
+ << QPoint(175, 200)
+ << QPoint(200, 175)
+ << QPoint(200, 228)
+ << QPoint(228, 200)
+ << QPoint(200, 122)
+ << QPoint(122, 200)
+ << QPoint(200, 280)
+ << QPoint(280, 200);
+ QTest::newRow("hollow square: testing points outside") << false << false << points;
+
+ points.clear();
+ points << QPoint(174, 174)
+ << QPoint(174, 225)
+ << QPoint(225, 174)
+ << QPoint(225, 225)
+ << QPoint(165, 200)
+ << QPoint(200, 165)
+ << QPoint(200, 235)
+ << QPoint(235, 200);
+ QTest::newRow("hollow circle: testing points inside") << true << true << points;
+
+ points.clear();
+ points << QPoint(160, 160)
+ << QPoint(160, 240)
+ << QPoint(240, 160)
+ << QPoint(240, 240)
+ << QPoint(200, 200)
+ << QPoint(185, 185)
+ << QPoint(185, 216)
+ << QPoint(216, 185)
+ << QPoint(216, 216)
+ << QPoint(145, 200)
+ << QPoint(200, 145)
+ << QPoint(255, 200)
+ << QPoint(200, 255);
+ QTest::newRow("hollow circle: testing points outside") << true << false << points;
+}
+
+void tst_QQuickItem::contains()
+{
+ QFETCH(bool, circleTest);
+ QFETCH(bool, insideTarget);
+ QFETCH(QList<QPoint>, points);
+
+ QQuickView *canvas = new QQuickView(0);
+ canvas->rootContext()->setContextProperty("circleShapeTest", circleTest);
+ canvas->setBaseSize(QSize(400, 400));
+ canvas->setSource(testFileUrl("hollowTestItem.qml"));
+ canvas->show();
+ canvas->requestActivateWindow();
+ QTest::qWaitForWindowShown(canvas);
+ QTRY_VERIFY(QGuiApplication::focusWindow() == canvas);
+
+ QQuickItem *root = qobject_cast<QQuickItem *>(canvas->rootObject());
+ QVERIFY(root);
+
+ HollowTestItem *hollowItem = root->findChild<HollowTestItem *>("hollowItem");
+ QVERIFY(hollowItem);
+
+ foreach (const QPoint &point, points) {
+ // check mouse hover
+ QTest::mouseMove(canvas, point);
+ QTest::qWait(10);
+ QCOMPARE(hollowItem->isHovered(), insideTarget);
+
+ // check mouse press
+ QTest::mousePress(canvas, Qt::LeftButton, 0, point);
+ QTest::qWait(10);
+ QCOMPARE(hollowItem->isPressed(), insideTarget);
+
+ // check mouse release
+ QTest::mouseRelease(canvas, Qt::LeftButton, 0, point);
+ QTest::qWait(10);
+ QCOMPARE(hollowItem->isPressed(), false);
+ }
+
+ delete canvas;
+}
+
+
QTEST_MAIN(tst_QQuickItem)
#include "tst_qquickitem.moc"