#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
}
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)
, inheritMirrorFromItem(false)
, isAccessible(false)
, culled(false)
+ , hasCursor(false)
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
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);
: rootItem(0)
, activeFocusItem(0)
, mouseGrabberItem(0)
+#ifndef QT_NO_CURSOR
+ , cursorItem(0)
+#endif
, touchMouseId(-1)
, touchMousePressTimestamp(0)
, renderWithoutShowing(false)
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();
}
#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)
void ignoreUnhandledMouseEvents();
void ownershipRootItem();
+
+#ifndef QT_NO_CURSOR
+ void cursor();
+#endif
+
private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
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"