aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickitem.cpp91
-rw-r--r--src/quick/items/qquickitem.h7
-rw-r--r--src/quick/items/qquickitem_p.h6
-rw-r--r--src/quick/items/qquickmousearea.cpp25
-rw-r--r--src/quick/items/qquickmousearea_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp50
-rw-r--r--src/quick/items/qquickwindow_p.h7
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp33
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp143
9 files changed, 345 insertions, 19 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index fefddad4b6..5930b5fb0b 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -68,6 +68,10 @@
#include <private/qqmlaccessors_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#ifndef QT_NO_CURSOR
+# include <QtGui/qcursor.h>
+#endif
+
#include <float.h>
// XXX todo Check that elements that create items handle memory correctly after visual ownership change
@@ -2266,6 +2270,10 @@ void QQuickItemPrivate::derefWindow()
}
if (c->mouseGrabberItem == q)
c->mouseGrabberItem = 0;
+#ifndef QT_NO_CURSOR
+ if (c->cursorItem == q)
+ c->cursorItem = 0;
+#endif
if ( hoverEnabled )
c->hoverItems.removeAll(q);
if (itemNodeInstance)
@@ -2429,6 +2437,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, inheritMirrorFromItem(false)
, isAccessible(false)
, culled(false)
+ , hasCursor(false)
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
@@ -5113,6 +5122,88 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
d->hoverEnabled = enabled;
}
+#ifndef QT_NO_CURSOR
+
+
+/*!
+ Returns the cursor shape for this item.
+
+ The mouse cursor will assume this shape when it is over this
+ item, unless an override cursor is set.
+ See the \l{Qt::CursorShape}{list of predefined cursor objects} for a
+ range of useful shapes.
+
+ If no cursor shape has been set this returns a cursor with the Qt::ArrowCursor shape, however
+ another cursor shape may be displayed if an overlapping item has a valid cursor.
+
+ \sa setCursor(), unsetCursor()
+*/
+
+QCursor QQuickItem::cursor() const
+{
+ Q_D(const QQuickItem);
+ return d->extra.isAllocated()
+ ? d->extra->cursor
+ : QCursor();
+}
+
+/*!
+ Sets the \a cursor shape for this item.
+
+ \sa cursor(), unsetCursor()
+*/
+
+void QQuickItem::setCursor(const QCursor &cursor)
+{
+ Q_D(QQuickItem);
+
+ Qt::CursorShape oldShape = d->extra.isAllocated() ? d->extra->cursor.shape() : Qt::ArrowCursor;
+
+ if (oldShape != cursor.shape() || oldShape >= Qt::LastCursor || cursor.shape() >= Qt::LastCursor) {
+ d->extra.value().cursor = cursor;
+ if (d->window) {
+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(d->window);
+ if (windowPrivate->cursorItem == this)
+ d->window->setCursor(cursor);
+ }
+ }
+
+ if (!d->hasCursor) {
+ d->hasCursor = true;
+ if (d->window) {
+ QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ if (contains(mapFromScene(pos)))
+ QQuickWindowPrivate::get(d->window)->updateCursor(pos);
+ }
+ }
+}
+
+/*!
+ Clears the cursor shape for this item.
+
+ \sa cursor(), setCursor()
+*/
+
+void QQuickItem::unsetCursor()
+{
+ Q_D(QQuickItem);
+ if (!d->hasCursor)
+ return;
+ d->hasCursor = false;
+ if (d->extra.isAllocated())
+ d->extra->cursor = QCursor();
+
+ if (d->window) {
+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(d->window);
+ if (windowPrivate->cursorItem == this) {
+ QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ windowPrivate->updateCursor(pos);
+ }
+ }
+}
+
+#endif
+
void QQuickItem::grabMouse()
{
Q_D(QQuickItem);
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 61bb3250ea..8854449d2b 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -80,6 +80,7 @@ private:
Q_DECLARE_PRIVATE(QQuickTransform)
};
+class QCursor;
class QQuickItemLayer;
class QQmlV8Function;
class QQuickState;
@@ -282,6 +283,12 @@ public:
bool acceptHoverEvents() const;
void setAcceptHoverEvents(bool enabled);
+#ifndef QT_NO_CURSOR
+ QCursor cursor() const;
+ void setCursor(const QCursor &cursor);
+ void unsetCursor();
+#endif
+
bool isUnderMouse() const;
void grabMouse();
void ungrabMouse();
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index adfe384b25..9dc7344b89 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -342,6 +342,9 @@ public:
QQuickLayoutMirroringAttached* layoutDirectionAttached;
QQuickItemKeyFilter *keyHandler;
mutable QQuickItemLayer *layer;
+#ifndef QT_NO_CURSOR
+ QCursor cursor;
+#endif
QPointF userTransformOriginPoint;
int effectRefCount;
@@ -408,7 +411,8 @@ public:
bool inheritMirrorFromItem:1;
bool isAccessible:1;
bool culled:1;
- // bool dummy:2
+ bool hasCursor:1;
+ // bool dummy:1
// Bit 32
enum DirtyType {
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 5cfd62ff8f..85a224fc93 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -1136,15 +1136,6 @@ void QQuickMouseArea::setHovered(bool h)
d->hovered = h;
emit hoveredChanged();
d->hovered ? emit entered() : emit exited();
-#ifndef QT_NO_CURSOR
- if (d->cursor) {
- if (d->hovered) {
- window()->setCursor(QCursor(*d->cursor));
- } else {
- window()->unsetCursor();
- }
- }
-#endif
}
}
@@ -1274,19 +1265,19 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p)
#ifndef QT_NO_CURSOR
Qt::CursorShape QQuickMouseArea::cursorShape() const
{
- Q_D(const QQuickMouseArea);
- if (d->cursor)
- return d->cursor->shape();
- return Qt::ArrowCursor;
+ return cursor().shape();
}
void QQuickMouseArea::setCursorShape(Qt::CursorShape shape)
{
- Q_D(QQuickMouseArea);
- setHoverEnabled(true);
- delete d->cursor;
- d->cursor = new QCursor(shape);
+ if (cursor().shape() == shape)
+ return;
+
+ setCursor(shape);
+
+ emit cursorShapeChanged();
}
+
#endif
/*!
diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h
index 8a8de3bdc3..3385485cac 100644
--- a/src/quick/items/qquickmousearea_p.h
+++ b/src/quick/items/qquickmousearea_p.h
@@ -144,7 +144,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseArea : public QQuickItem
Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged)
Q_PROPERTY(bool propagateComposedEvents READ propagateComposedEvents WRITE setPropagateComposedEvents NOTIFY propagateComposedEventsChanged)
#ifndef QT_NO_CURSOR
- Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY cursorShapeChanged)
+ Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape RESET unsetCursor NOTIFY cursorShapeChanged)
#endif
public:
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 1d3adea5cb..4cfe1a101c 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -326,6 +326,9 @@ QQuickWindowPrivate::QQuickWindowPrivate()
: rootItem(0)
, activeFocusItem(0)
, mouseGrabberItem(0)
+#ifndef QT_NO_CURSOR
+ , cursorItem(0)
+#endif
, touchMouseId(-1)
, touchMousePressTimestamp(0)
, renderWithoutShowing(false)
@@ -1301,6 +1304,10 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
#endif
+#ifndef QT_NO_CURSOR
+ d->updateCursor(event->windowPos());
+#endif
+
if (!d->mouseGrabberItem) {
if (d->lastMousePosition.isNull())
d->lastMousePosition = event->windowPos();
@@ -1832,6 +1839,49 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
}
#endif // QT_NO_DRAGANDDROP
+#ifndef QT_NO_CURSOR
+void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
+{
+ Q_Q(QQuickWindow);
+
+ QQuickItem *oldCursorItem = cursorItem;
+ cursorItem = findCursorItem(rootItem, scenePos);
+
+ if (cursorItem != oldCursorItem) {
+ if (cursorItem)
+ q->setCursor(cursorItem->cursor());
+ else
+ q->unsetCursor();
+ }
+}
+
+QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF &scenePos)
+{
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (!item->contains(p))
+ return 0;
+ }
+
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled())
+ continue;
+ if (QQuickItem *cursorItem = findCursorItem(child, scenePos))
+ return cursorItem;
+ }
+
+ if (itemPrivate->hasCursor) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (item->contains(p))
+ return item;
+ }
+ return 0;
+}
+#endif
+
bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event)
{
if (!target)
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 52e46ca6a8..267c8d3104 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -113,6 +113,9 @@ public:
// Keeps track of the item currently receiving mouse events
QQuickItem *mouseGrabberItem;
+#ifndef QT_NO_CURSOR
+ QQuickItem *cursorItem;
+#endif
#ifndef QT_NO_DRAGANDDROP
QQuickDragGrabber dragGrabber;
#endif
@@ -145,6 +148,10 @@ public:
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *);
#endif
+#ifndef QT_NO_CURSOR
+ void updateCursor(const QPointF &scenePos);
+ QQuickItem *findCursorItem(QQuickItem *item, const QPointF &scenePos);
+#endif
QList<QQuickItem*> hoverItems;
enum FocusOption {
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 67a36af6bb..f7ccd67f5f 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -85,6 +85,9 @@ private slots:
void pressedMultipleButtons_data();
void pressedMultipleButtons();
void changeAxis();
+#ifndef QT_NO_CURSOR
+ void cursorShape();
+#endif
private:
void acceptedButton_data();
@@ -1366,6 +1369,36 @@ void tst_QQuickMouseArea::changeAxis()
delete view;
}
+#ifndef QT_NO_CURSOR
+void tst_QQuickMouseArea::cursorShape()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\n MouseArea {}", QUrl());
+ QScopedPointer<QObject> object(component.create());
+ QQuickMouseArea *mouseArea = qobject_cast<QQuickMouseArea *>(object.data());
+ QVERIFY(mouseArea);
+
+ QSignalSpy spy(mouseArea, SIGNAL(cursorShapeChanged()));
+
+ QCOMPARE(mouseArea->cursorShape(), Qt::ArrowCursor);
+ QCOMPARE(mouseArea->cursor().shape(), Qt::ArrowCursor);
+
+ mouseArea->setCursorShape(Qt::IBeamCursor);
+ QCOMPARE(mouseArea->cursorShape(), Qt::IBeamCursor);
+ QCOMPARE(mouseArea->cursor().shape(), Qt::IBeamCursor);
+ QCOMPARE(spy.count(), 1);
+
+ mouseArea->setCursorShape(Qt::IBeamCursor);
+ QCOMPARE(spy.count(), 1);
+
+ mouseArea->setCursorShape(Qt::WaitCursor);
+ QCOMPARE(mouseArea->cursorShape(), Qt::WaitCursor);
+ QCOMPARE(mouseArea->cursor().shape(), Qt::WaitCursor);
+ QCOMPARE(spy.count(), 2);
+}
+#endif
+
QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc"
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 9800c724b9..01d60361b4 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -310,6 +310,11 @@ private slots:
void ignoreUnhandledMouseEvents();
void ownershipRootItem();
+
+#ifndef QT_NO_CURSOR
+ void cursor();
+#endif
+
private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
@@ -1061,6 +1066,144 @@ void tst_qquickwindow::ownershipRootItem()
QCoreApplication::processEvents();
QVERIFY(!accessor->isRootItemDestroyed());
}
+
+#ifndef QT_NO_CURSOR
+void tst_qquickwindow::cursor()
+{
+ QQuickWindow window;
+ window.resize(320, 240);
+
+ QQuickItem parentItem;
+ parentItem.setPos(QPointF(0, 0));
+ parentItem.setSize(QSizeF(180, 180));
+ parentItem.setParentItem(window.rootItem());
+
+ QQuickItem childItem;
+ childItem.setPos(QPointF(60, 90));
+ childItem.setSize(QSizeF(120, 120));
+ childItem.setParentItem(&parentItem);
+
+ QQuickItem clippingItem;
+ clippingItem.setPos(QPointF(120, 120));
+ clippingItem.setSize(QSizeF(180, 180));
+ clippingItem.setClip(true);
+ clippingItem.setParentItem(window.rootItem());
+
+ QQuickItem clippedItem;
+ clippedItem.setPos(QPointF(-30, -30));
+ clippedItem.setSize(QSizeF(120, 120));
+ clippedItem.setParentItem(&clippingItem);
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ // Position the cursor over the parent and child item and the clipped section of clippedItem.
+ QTest::mouseMove(&window, QPoint(100, 100));
+
+ // No items cursors, window cursor is the default arrow.
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ // The section of clippedItem under the cursor is clipped, and so doesn't affect the window cursor.
+ clippedItem.setCursor(Qt::ForbiddenCursor);
+ QCOMPARE(clippedItem.cursor().shape(), Qt::ForbiddenCursor);
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ // parentItem is under the cursor, so the window cursor is changed.
+ parentItem.setCursor(Qt::IBeamCursor);
+ QCOMPARE(parentItem.cursor().shape(), Qt::IBeamCursor);
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
+
+ // childItem is under the cursor and is in front of its parent, so the window cursor is changed.
+ childItem.setCursor(Qt::WaitCursor);
+ QCOMPARE(childItem.cursor().shape(), Qt::WaitCursor);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ childItem.setCursor(Qt::PointingHandCursor);
+ QCOMPARE(childItem.cursor().shape(), Qt::PointingHandCursor);
+ QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
+
+ // childItem is the current cursor item, so this has no effect on the window cursor.
+ parentItem.unsetCursor();
+ QCOMPARE(parentItem.cursor().shape(), Qt::ArrowCursor);
+ QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
+
+ parentItem.setCursor(Qt::IBeamCursor);
+ QCOMPARE(parentItem.cursor().shape(), Qt::IBeamCursor);
+ QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
+
+ // With the childItem cursor cleared, parentItem is now foremost.
+ childItem.unsetCursor();
+ QCOMPARE(childItem.cursor().shape(), Qt::ArrowCursor);
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
+
+ // Setting the childItem cursor to the default still takes precedence over parentItem.
+ childItem.setCursor(Qt::ArrowCursor);
+ QCOMPARE(childItem.cursor().shape(), Qt::ArrowCursor);
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ childItem.setCursor(Qt::WaitCursor);
+ QCOMPARE(childItem.cursor().shape(), Qt::WaitCursor);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ // Move the cursor so it is over just parentItem.
+ QTest::mouseMove(&window, QPoint(20, 20));
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
+
+ // Move the cursor so that is over all items, clippedItem wins because its a child of
+ // clippingItem which is in from of parentItem in painting order.
+ QTest::mouseMove(&window, QPoint(125, 125));
+ QCOMPARE(window.cursor().shape(), Qt::ForbiddenCursor);
+
+ // Over clippingItem only, so no cursor.
+ QTest::mouseMove(&window, QPoint(200, 280));
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ // Over no item, so no cursor.
+ QTest::mouseMove(&window, QPoint(10, 280));
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+
+ // back to the start.
+ QTest::mouseMove(&window, QPoint(100, 100));
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ // Try with the mouse pressed.
+ QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100, 100));
+ QTest::mouseMove(&window, QPoint(20, 20));
+ QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
+ QTest::mouseMove(&window, QPoint(125, 125));
+ QCOMPARE(window.cursor().shape(), Qt::ForbiddenCursor);
+ QTest::mouseMove(&window, QPoint(200, 280));
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+ QTest::mouseMove(&window, QPoint(10, 280));
+ QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
+ QTest::mouseMove(&window, QPoint(100, 100));
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100, 100));
+
+ // Remove the cursor item from the scene. Theoretically this should make parentItem the
+ // cursorItem, but given the situation will correct itself after the next mouse move it's
+ // probably better left as is to avoid unnecessary work during tear down.
+ childItem.setParentItem(0);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ parentItem.setCursor(Qt::SizeAllCursor);
+ QCOMPARE(parentItem.cursor().shape(), Qt::SizeAllCursor);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ // Changing the cursor of an un-parented item doesn't affect the window's cursor.
+ childItem.setCursor(Qt::ClosedHandCursor);
+ QCOMPARE(childItem.cursor().shape(), Qt::ClosedHandCursor);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ childItem.unsetCursor();
+ QCOMPARE(childItem.cursor().shape(), Qt::ArrowCursor);
+ QCOMPARE(window.cursor().shape(), Qt::WaitCursor);
+
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100, 101));
+ QCOMPARE(window.cursor().shape(), Qt::SizeAllCursor);
+}
+#endif
+
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"