diff options
author | Robin Burchell <robin.burchell@theqtcompany.com> | 2015-10-14 20:58:21 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2016-05-26 17:37:41 +0000 |
commit | 6f84a09dfbd15aac023580cf06e7b8c24f3b524c (patch) | |
tree | eb34fd782c151b6d9c1254428efd96353c44b255 | |
parent | 0ac9389dbd7facaa424dc175e5f5dac514630eef (diff) |
QQuickWindow: Process a synthetic hover once per frame
This removes the limitation that hover only changes when the mouse moves, and
(in my opinion) is required to make hover functionality even remotely useful.
QtQuick scenes are a dynamic medium, items can move (or change) frequently, so
only changing this in response to a user action means it will frequently end up
out of date.
A very simple example of this is a ListView of delegates that each have a
background set on hover: when flicking the list, it won't reset the hover to
actually match the item under the mouse.
This is now not a (very) expensive operation to do, as deliverHoverEvent
does nothing if the item tree in question doesn't have hover enabled.
[ChangeLog][QtQuick][MouseArea] Hover state is now updated once per frame.
This means that MouseArea::containsMouse property will now be correct even
if the mouse is not moving, but items move under the cursor. Likewise
the mouse position properties and positionChanged signal will act as if
the mouse had moved.
Task-number: QTBUG-40475
Task-number: QTBUG-42194
Task-number: QTBUG-33982
Task-number: QTBUG-42578
Task-number: QTBUG-52537
Change-Id: Ic2dcbb45339e11c07f5c6a9c95eb7f64957968eb
Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
Reviewed-by: Robin Burchell <robin.burchell@viroteck.net>
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 5 | ||||
-rw-r--r-- | src/quick/items/qquickrendercontrol.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 13 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgrenderloop.cpp | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgthreadedrenderloop.cpp | 2 | ||||
-rw-r--r-- | src/quick/scenegraph/qsgwindowsrenderloop.cpp | 2 |
7 files changed, 19 insertions, 9 deletions
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 557922a861..66aff5c2f4 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -837,7 +837,7 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) Q_D(QQuickMouseArea); if (!d->enabled && !d->pressed) { QQuickItem::hoverMoveEvent(event); - } else { + } else if (d->lastPos != event->posF()) { d->lastPos = event->posF(); d->lastModifiers = event->modifiers(); QQuickMouseEvent &me = d->quickMouseEvent; @@ -1083,8 +1083,7 @@ void QQuickMouseArea::setHoverEnabled(bool h) \qmlproperty bool QtQuick::MouseArea::containsMouse This property holds whether the mouse is currently inside the mouse area. - \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. - In addition, if hoverEnabled is false, containsMouse will only be valid + \warning If hoverEnabled is false, containsMouse will only be valid when the mouse is pressed while the mouse cursor is inside the MouseArea. */ bool QQuickMouseArea::hovered() const diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index e36df53d38..de10b80451 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -247,7 +247,7 @@ void QQuickRenderControl::polishItems() return; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); if (!d->window) return; cd->polishItems(); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 48e5b54016..c132d59cc8 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2021,7 +2021,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) } } -void QQuickWindowPrivate::flushDelayedTouchEvent() +void QQuickWindowPrivate::flushFrameSynchronousEvents() { if (delayedTouch) { deliverDelayedTouchEvent(); @@ -2032,6 +2032,17 @@ void QQuickWindowPrivate::flushDelayedTouchEvent() if (ut && ut->hasStartAnimationPending()) ut->startAnimations(); } + + // Once per frame, send a synthetic hover, in case items have changed position. + // For instance, during animation (including the case of a ListView + // whose delegates contain MouseAreas), a MouseArea needs to know + // whether it has moved into a position where it is now under the cursor. + if (!mouseGrabberItem && !lastMousePosition.isNull()) { + bool accepted = false; + bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), accepted); + if (!delivered) + clearHover(); // take care of any exits + } } void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1486e20e1e..daff9ef473 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -160,7 +160,7 @@ public: void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); - void flushDelayedTouchEvent(); + void flushFrameSynchronousEvents(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 1b0b1acc0e..fbb0fbb98a 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -371,7 +371,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) return; if (!data.grabOnly) { - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); // Event delivery/processing triggered the window to be deleted or stop rendering. if (!m_windows.contains(window)) return; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 7c3405b715..aa27804a1e 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -1141,7 +1141,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) } // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); // The delivery of the event might have caused the window to stop rendering w = windowFor(m_windows, window); if (!w || !w->thread || !w->thread->window) { diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 901fbbec43..1a0ab23a72 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -434,7 +434,7 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) } } - d->flushDelayedTouchEvent(); + d->flushFrameSynchronousEvents(); // Event delivery or processing has caused the window to stop rendering. if (!windowData(window)) return; |