diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2018-09-26 17:36:04 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2018-10-04 07:00:21 +0000 |
commit | 21355b3630882932be940a48a88d8c40cf7ebf63 (patch) | |
tree | 7228a7b44299f02a500f7502db509e84d262e290 | |
parent | d37d8e962a1b9e83800d17207c78bc87f7d64c31 (diff) | |
download | qtbase-21355b3630882932be940a48a88d8c40cf7ebf63.tar.gz |
QGraphicsScene: Make focusing on touchBegin optional
Usually we focus in when we receive a click or equivalent.
QGraphicsScene by default also transfers the focus when you start a
touch on a trackpad or similar. Most of the time this also generates a
synthetic mouse click, so people don't necessary notice. However, at
least on macOS you can configure this behavior. With focusOnTouch
switched off, QGraphicsScene behaves as one would expect on macOS.
Fixes: QTBUG-59442
Change-Id: Ib87112640eef6b77892ad2490d80eedd055e6dce
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Venugopal Shivashankar <Venugopal.Shivashankar@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene.cpp | 91 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene.h | 4 | ||||
-rw-r--r-- | src/widgets/graphicsview/qgraphicsscene_p.h | 3 | ||||
-rw-r--r-- | tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp | 36 |
4 files changed, 107 insertions, 27 deletions
diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index 37af4ecda0..bba992144d 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -297,6 +297,7 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() painterStateProtection(true), sortCacheEnabled(false), allItemsIgnoreTouchEvents(true), + focusOnTouch(true), minimumRenderSize(0.0), selectionChanging(0), rectAdjust(2), @@ -2393,6 +2394,7 @@ void QGraphicsScene::clear() d->allItemsIgnoreHoverEvents = true; d->allItemsUseDefaultCursor = true; d->allItemsIgnoreTouchEvents = true; + d->focusOnTouch = true; } /*! @@ -5854,6 +5856,41 @@ void QGraphicsScene::setMinimumRenderSize(qreal minSize) update(); } +/*! + \property QGraphicsScene::focusOnTouch + \since 5.12 + \brief whether items gain focus when receiving a \e {touch begin} event. + + The usual behavior is to transfer focus only when an item is clicked. Often + a tap on a touchpad is interpreted as equivalent to a mouse click by the + operating system, generating a synthesized click event in response. However, + at least on macOS you can configure this behavior. + + By default, QGraphicsScene also transfers focus when you touch on a trackpad + or similar. If the operating system is configured to not generate a + synthetic mouse click on tapping the trackpad, this is surprising. If the + operating system does generate synthetic mouse clicks on tapping the + trackpad, the focus transfer on starting a touch gesture is unnecessary. + + With focusOnTouch switched off, QGraphicsScene behaves as one would expect + on macOS. + + The default value is \c true, ensuring that the default behavior is just as + in Qt versions prior to 5.12. Set to \c false to prevent touch events from + triggering focus changes. +*/ +bool QGraphicsScene::focusOnTouch() const +{ + Q_D(const QGraphicsScene); + return d->focusOnTouch; +} + +void QGraphicsScene::setFocusOnTouch(bool enabled) +{ + Q_D(QGraphicsScene); + d->focusOnTouch = enabled; +} + void QGraphicsScenePrivate::addView(QGraphicsView *view) { views << view; @@ -6033,39 +6070,41 @@ bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEve { Q_Q(QGraphicsScene); - if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.constFirst() != origin) { - const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first(); - cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(), - firstTouchPoint.scenePos(), - static_cast<QWidget *>(touchEvent->target())); - } + if (focusOnTouch) { + if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.constFirst() != origin) { + const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first(); + cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(), + firstTouchPoint.scenePos(), + static_cast<QWidget *>(touchEvent->target())); + } - // Set focus on the topmost enabled item that can take focus. - bool setFocus = false; + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; - foreach (QGraphicsItem *item, cachedItemsUnderMouse) { - if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { - if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + if (item->isPanel()) + break; + if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation) + break; + if (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling) { + // Make sure we don't clear focus. setFocus = true; - if (item != q->focusItem()) - q->setFocusItem(item, Qt::MouseFocusReason); break; } } - if (item->isPanel()) - break; - if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation) - break; - if (item->d_ptr->flags & QGraphicsItem::ItemStopsFocusHandling) { - // Make sure we don't clear focus. - setFocus = true; - break; - } - } - // If nobody could take focus, clear it. - if (!stickyFocus && !setFocus) - q->setFocusItem(0, Qt::MouseFocusReason); + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + } bool res = false; bool eventAccepted = touchEvent->isAccepted(); diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h index 8efbcd273e..287e551db7 100644 --- a/src/widgets/graphicsview/qgraphicsscene.h +++ b/src/widgets/graphicsview/qgraphicsscene.h @@ -106,6 +106,7 @@ class Q_WIDGETS_EXPORT QGraphicsScene : public QObject Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled) Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus) Q_PROPERTY(qreal minimumRenderSize READ minimumRenderSize WRITE setMinimumRenderSize) + Q_PROPERTY(bool focusOnTouch READ focusOnTouch WRITE setFocusOnTouch) public: enum ItemIndexMethod { @@ -253,6 +254,9 @@ public: qreal minimumRenderSize() const; void setMinimumRenderSize(qreal minSize); + bool focusOnTouch() const; + void setFocusOnTouch(bool enabled); + public Q_SLOTS: void update(const QRectF &rect = QRectF()); void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers); diff --git a/src/widgets/graphicsview/qgraphicsscene_p.h b/src/widgets/graphicsview/qgraphicsscene_p.h index 2f5d7c54bb..a2d13436fc 100644 --- a/src/widgets/graphicsview/qgraphicsscene_p.h +++ b/src/widgets/graphicsview/qgraphicsscene_p.h @@ -114,7 +114,8 @@ public: quint32 painterStateProtection : 1; quint32 sortCacheEnabled : 1; // for compatibility quint32 allItemsIgnoreTouchEvents : 1; - quint32 padding : 15; + quint32 focusOnTouch : 1; + quint32 padding : 14; qreal minimumRenderSize; diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp index c8ee2d65a3..838b1f4be6 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -254,6 +254,7 @@ private slots: void zeroScale(); void focusItemChangedSignal(); void minimumRenderSize(); + void focusOnTouch(); // task specific tests below me void task139710_bspTreeCrash(); @@ -4758,6 +4759,41 @@ void tst_QGraphicsScene::minimumRenderSize() QVERIFY(smallChild->repaints > smallerGrandChild->repaints); } +void tst_QGraphicsScene::focusOnTouch() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + scene.setSceneRect(0, 0, 100, 100); + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + rect->setFlag(QGraphicsItem::ItemIsFocusable, true); + + view.show(); + QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QVERIFY(!rect->hasFocus()); + + scene.setFocusOnTouch(false); + + QTouchDevice device; + device.setType(QTouchDevice::TouchPad); + QList<QTouchEvent::TouchPoint> touchPoints; + QTouchEvent::TouchPoint point; + point.setScenePos(QPointF(10, 10)); + point.setState(Qt::TouchPointPressed); + touchPoints.append(point); + QTouchEvent event(QEvent::TouchBegin, &device, Qt::NoModifier, Qt::TouchPointStates(), + touchPoints); + + QApplication::sendEvent(&scene, &event); + + QVERIFY(!rect->hasFocus()); + scene.setFocusOnTouch(true); + + QApplication::sendEvent(&scene, &event); + QVERIFY(rect->hasFocus()); +} + void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache() { QGraphicsScene scene; |