QQuickHoverHandler: don't use passive grabs

QQuickDeliveryAgent will clear the list of passive grabbers
when we deliver:
1. a QPointerEvent::isEndEvent() from deliverPointerEvent()
2. a QEventPoint::Pressed event from deliverPressOrReleaseEvent()

In other words, QQuickDeliveryAgent will clear the list of grabbers
whenever it receives a mouse press or release. This doesn't work
well with hover handlers, which were using passive grabs to
ensure receiving updates: they also lost their grabs on press and
release.  This has some implications:

1. the list of hover items (QQuickDeliveryAgentPrivate::hoverItems)
    will no longer be in sync with the items we deliver events to.
2. a hover handler stacked underneath another hover handler
    will stop working. The reason is that QQuickDeliveryAgent
    detects that hoverItems is not empty, and as such, assumes
    that all handlers will receive events from their passive grabs.
    (which is no longer the case after the clear)

So letting hover handlers rely on passive grabbing currently fails.
It was also confusing that we delivered some of the hover events
from deliverHoverEvent(), and others from passive grabs in
deliverPointerEvent(). In Qt Quick 3D, when the hover is delivered
because of a passive grab, we need to use sceneTransform; but when
picking is done, the transform was already done at the same time.
But hover events that come from flushFrameSynchronousEvents()
always need to go through picking, and that happens frequently,
so it's more consistent if we just rely on it all the time.

In addition, the previous solution was assuming that only one leaf item
would be under the mouse. This fails when you have siblings that overlap
(and each sibling has HoverHandlers).

While we could try to be more careful about when, and which, grabbers
we clear here and there from QQuickDeliveryAgent, it seems better to
dodge the whole passive grabber logic for hover handlers, and instead
send all hover events directly from deliverHoverEvent(). This because
we anyway need to traverse all the items in the application on each
pointer move to check if new items are being hovered. So we might as
well send out hover events in the same go. That way the logic becomes
a bit easier to follow, and don't need to worry about keeping the
hoveredItems list in sync with passive grabbing.

tst_qquickhoverhandler:
hoverHandlerAndUnderlyingMouseArea:
- HoverHandlers have (conceptually) never stopped hover events
  from propagating to the parent. Still, this test checks that
  a MouseArea underneath a HoverHandler is not hovered. Since
  this now actually works, the test is changed.

mouseAreaAndUnderlyingHoverHandler:
- MouseArea now accepts hover events, which will stop propagation.
  This is done to preserve the same behavior as before. But this
  also means that a MouseArea that has another MouseArea as a direct
  child (which was a special case from before) will no longer get hover
  events after the child has accepted them. For the same reason, an
  item's HoverHandlers will also not get hover events if there is a
  child that is accepting them, as in this test case.

Fixes: QTBUG-34882
Fixes: QTBUG-63670
Pick-to: 6.2
Change-Id: Id38042bcbd1c3ca5403b4b81b875b84196fcfc76
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
This commit is contained in:
Richard Moe Gustavsen 2021-02-25 17:43:36 +01:00 committed by Shawn Rutledge
parent 9d9ebbff2f
commit bbcc2657fa
5 changed files with 231 additions and 111 deletions

View File

@ -139,7 +139,6 @@ void QQuickHoverHandler::handleEventPoint(QPointerEvent *ev, QEventPoint &point)
else if (QQuickDeliveryAgentPrivate::isTabletEvent(ev))
m_hoveredTablet = true;
setHovered(hovered);
setPassiveGrab(ev, point);
}
/*!

View File

@ -848,6 +848,15 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
emit mouseYChanged(&me);
me.setPosition(d->lastPos);
}
if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
if (parentMouseArea->acceptHoverEvents()) {
// Special legacy case: if our parent is another MouseArea, and we're
// hovered, the parent MouseArea should be hovered too. We achieve this
// by simply ignoring the event to not block propagation.
event->ignore();
}
}
}
void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
@ -866,6 +875,15 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
me.setPosition(d->lastPos);
emit positionChanged(&me);
}
if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
if (parentMouseArea->acceptHoverEvents()) {
// Special legacy case: if our parent is another MouseArea, and we're
// hovered, the parent MouseArea should be hovered too. We achieve this
// by simply ignoring the event to not block propagation.
event->ignore();
}
}
}
void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
@ -875,6 +893,15 @@ void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
QQuickItem::hoverLeaveEvent(event);
else
setHovered(false);
if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
if (parentMouseArea->acceptHoverEvents()) {
// Special legacy case: if our parent is another MouseArea, and we're
// hovered, the parent MouseArea should be hovered too. We achieve this
// by simply ignoring the event to not block propagation.
event->ignore();
}
}
}
#if QT_CONFIG(wheelevent)

View File

@ -224,11 +224,7 @@ bool QQuickDeliveryAgentPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEve
QPointF last = lastMousePosition;
lastMousePosition = me.scenePosition();
bool accepted = me.isAccepted();
bool delivered = deliverHoverEvent(rootItem, me.scenePosition(), last, me.modifiers(), me.timestamp(), accepted);
// take care of any exits
if (!delivered)
clearHover(me.timestamp());
deliverHoverEvent(me.scenePosition(), last, me.modifiers(), me.timestamp());
break;
}
} else if (p.state() & QEventPoint::State::Released) {
@ -602,15 +598,18 @@ bool QQuickDeliveryAgentPrivate::clearHover(ulong timestamp)
if (!window)
return false;
QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
const auto modifiers = QGuiApplication::keyboardModifiers();
const bool clearHover = true;
bool accepted = false;
for (QQuickItem* item : qAsConst(hoverItems)) {
if (item)
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp) || accepted;
for (auto hoverItem : hoverItems) {
auto item = hoverItem.first;
deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, clearHover);
}
hoverItems.clear();
return accepted;
return true;
}
void QQuickDeliveryAgentPrivate::updateFocusItemTransform()
@ -710,17 +709,16 @@ bool QQuickDeliveryAgent::event(QEvent *ev)
case QEvent::HoverLeave:
case QEvent::HoverMove: {
QHoverEvent *he = static_cast<QHoverEvent*>(ev);
bool accepted = he->isAccepted();
bool delivered = d->deliverHoverEvent(d->rootItem, he->scenePosition(),
bool accepted = d->deliverHoverEvent(he->scenePosition(),
he->points().first().sceneLastPosition(),
he->modifiers(), he->timestamp(), accepted);
he->modifiers(), he->timestamp());
d->lastMousePosition = he->scenePosition();
he->setAccepted(accepted);
#if QT_CONFIG(cursor)
QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(d->sceneTransform ?
d->sceneTransform->map(he->scenePosition()) : he->scenePosition(), d->rootItem);
#endif
return delivered;
return accepted;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
@ -742,16 +740,15 @@ bool QQuickDeliveryAgent::event(QEvent *ev)
if (!d->rootItem)
return false;
QEnterEvent *enter = static_cast<QEnterEvent*>(ev);
bool accepted = enter->isAccepted();
bool delivered = d->deliverHoverEvent(d->rootItem, enter->scenePosition(),
bool accepted = d->deliverHoverEvent(enter->scenePosition(),
enter->points().first().sceneLastPosition(),
enter->modifiers(), enter->timestamp(), accepted);
enter->modifiers(), enter->timestamp());
d->lastMousePosition = enter->scenePosition();
enter->setAccepted(accepted);
#if QT_CONFIG(cursor)
QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(enter->scenePosition(), d->rootItem);
#endif
return delivered;
return accepted;
}
case QEvent::Leave:
d->clearHover();
@ -949,95 +946,197 @@ bool QQuickDeliveryAgentPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *i
return hoverEvent.isAccepted();
}
/*! \internal
Delivers a hover event at \a scenePos to the whole scene or subscene
that this DeliveryAgent is responsible for. Returns \c true if
delivery is "done".
*/
// TODO later: specify the device in case of multi-mouse scenario, or mouse and tablet both in use
bool QQuickDeliveryAgentPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp)
{
Q_Q(QQuickDeliveryAgent);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
const QPointF itemPos = item->mapFromScene(sceneTransform ? sceneTransform->map(scenePos) : scenePos);
if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape || item == rootItem) && !item->contains(itemPos))
if (!QQuickItemPrivate::get(rootItem)->subtreeHoverEnabled)
return false;
if (Q_UNLIKELY(lcHoverTrace().isDebugEnabled())) {
if (lastScenePos == scenePos)
qCDebug(lcHoverTrace) << q << scenePos << "(unchanged)" << "," << itemPos << "in" << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
else
qCDebug(lcHoverTrace) << q << lastScenePos << "->" << scenePos << "," << itemPos << "in" << item << ", subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
}
if (itemPrivate->subtreeHoverEnabled) {
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
if (!child->isVisible() || QQuickItemPrivate::get(child)->culled)
continue;
if (!child->isEnabled() && !QQuickItemPrivate::get(child)->subtreeHoverEnabled)
continue;
if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
return true;
// The first time this function is called, hoverItems is empty.
// We then call deliverHoverEventRecursive from the rootItem, and
// populate the list with all the children and grandchildren that
// we find that should receive hover events (in addition to sending
// hover events to them and their HoverHandlers). We also set the
// hoverId for each item to the currentHoverId.
// The next time this function is called, we bump currentHoverId,
// and call deliverHoverEventRecursive once more.
// When that call returns, the list will contain the items that
// were hovered the first time, as well as the items that were hovered
// this time. But only the items that were hovered this time
// will have their hoverId equal to currentHoverId; the ones we didn't
// visit will still have an old hoverId. We can therefore go through the
// list at the end of this function and look for items with an old hoverId,
// remove them from the list, and update their state accordingly.
currentHoverId++;
const bool itemsWasHovered = !hoverItems.isEmpty();
deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
// Prune the list for items that are no longer hovered
for (auto it = hoverItems.begin(); it != hoverItems.end();) {
auto item = (*it).first.data();
auto hoverId = (*it).second;
if (hoverId == currentHoverId) {
// Still being hovered
it++;
} else {
// No longer hovered. If hoverId is 0, it means that we have sent a HoverLeave
// event to the item already, and it can just be removed from the list. Note that
// the item can have been deleted as well.
if (item && hoverId != 0) {
const bool clearHover = true;
deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, clearHover);
}
it = hoverItems.erase(it);
}
}
if (itemPrivate->hasPointerHandlers()) {
QMouseEvent hoverEvent(QEvent::MouseMove, itemPos, scenePos,
itemPrivate->window->mapToGlobal(scenePos), Qt::NoButton, Qt::NoButton, modifiers);
ensureDeviceConnected(hoverEvent.pointingDevice());
hoverEvent.setTimestamp(timestamp);
hoverEvent.setAccepted(true);
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
hh->handlePointerEvent(&hoverEvent);
const bool itemsAreHovered = !hoverItems.isEmpty();
return itemsWasHovered || itemsAreHovered;
}
/*! \internal
Delivers a hover event at \a scenePos to \a item and all its children.
The children get it first. As soon as any item allows the event to remain
accepted, recursion stops. Returns \c true in that case, or \c false if the
event is rejected.
All items that have hover enabled (either explicitly, from
setAcceptHoverEvents(), or implicitly by having HoverHandlers) will have
the QQuickItemPrivate::hoverEnabled flag set. And all their anchestors will
have the QQuickItemPrivate::subtreeHoverEnabledset. This function will
follow the subtrees that have subtreeHoverEnabled by recursing into each
child with that flag set. And for each child (in addition to the item
itself) that also has hoverEnabled set, we call deliverHoverEventToItem()
to actually deliver the event to it. The item can then choose to accept or
reject the event. This is only for control over whether we stop propagation
or not: an item can reject the event, but at the same time be hovered (and
therefore in hoverItems). By accepting the event, the item will effectivly
end up as the only one hovered. Any other HoverHandler that may be a child
of an item that is stacked underneath, will not. Note that since siblings
can overlap, there can be more than one leaf item under the mouse.
*/
bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive(
QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp)
{
const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
const QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
const QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || childPrivate->culled)
continue;
if (!child->isEnabled() && !childPrivate->subtreeHoverEnabled)
continue;
if (childPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
const QPointF localPos = child->mapFromScene(scenePos);
if (!child->contains(localPos))
continue;
}
// Recurse into the child
const bool accepted = deliverHoverEventRecursive(child, scenePos, lastScenePos, modifiers, timestamp);
if (accepted) {
// Stop propagation / recursion
return true;
}
}
if (itemPrivate->hoverEnabled && item->contains(itemPos)) {
if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
//move
// All decendants have been visited.
// Now deliver the event to the item
if (itemPrivate->hoverEnabled)
return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, false);
// Continue propagation / recursion
return false;
}
/*! \internal
Delivers a hover event at \a scenePos to \a item and its HoverHandlers if any.
Returns \c true if the event remains accepted, \c false if rejected.
If \a clearHover is \c true, it will be sent as a QEvent::HoverLeave event,
and the item and its handlers are expected to transition into their non-hovered
states even if the position still indicates that the mouse is inside.
*/
bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
const QPointF localPos = item->mapFromScene(scenePos);
const QPointF globalPos = itemPrivate->window->mapToGlobal(scenePos);
const bool isHovering = item->contains(localPos);
const bool wasHovering = hoverItems.contains(item);
qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos
<< "wasHovering:" << wasHovering << "isHovering:" << isHovering;
// Send enter/move/leave event to the item
bool accepted = false;
if (isHovering && !clearHover) {
// Add the item to the list of hovered items (if it doesn't exist there
// from before), and update hoverId to mark that it's (still) hovered.
hoverItems[item] = currentHoverId;
if (wasHovering)
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
} else {
QList<QQuickItem *> itemsToHover;
QQuickItem* parent = item;
itemsToHover << item;
while ((parent = parent->parentItem()))
itemsToHover << parent;
else
accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, timestamp);
} else if (wasHovering) {
// A leave should never stop propagation
hoverItems[item] = 0;
sendHoverEvent(QEvent::HoverLeave, item, scenePos, lastScenePos, modifiers, timestamp);
}
// Leaving from previous hovered items until we reach the item or one of its ancestors.
while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
if (hoverLeaveItem)
sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp);
if (!itemPrivate->hasPointerHandlers())
return accepted;
// If the item didn't accept the hover event, 'accepted' is now false.
// Otherwise it's true, and then it should stay the way regardless of
// whether or not the hoverhandlers themselves are hovered.
// Note that since a HoverHandler can have a margin, a HoverHandler
// can be hovered even if the item itself is not.
if (clearHover) {
// Note: a leave should never stop propagation
QHoverEvent hoverEvent(QEvent::HoverLeave, scenePos, lastScenePos, modifiers);
hoverEvent.setTimestamp(timestamp);
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
hoverEvent.setAccepted(true);
QCoreApplication::sendEvent(hh, &hoverEvent);
}
}
} else {
QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, Qt::NoButton, modifiers);
hoverEvent.setTimestamp(timestamp);
if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
// ### Shouldn't we send moves for the parent items as well?
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
} else {
// Enter items that are not entered yet.
int startIdx = -1;
if (!hoverItems.isEmpty())
startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
if (startIdx == -1)
startIdx = itemsToHover.count() - 1;
for (int i = startIdx; i >= 0; i--) {
QQuickItem *itemToHover = itemsToHover.at(i);
QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
// The item may be about to be deleted or reparented to another window
// due to another hover event delivered in this function. If that is the
// case, sending a hover event here will cause a crash or other bad
// behavior when the leave event is generated. Checking
// itemToHoverPrivate->window here prevents that case.
if (itemToHoverPrivate->window == itemPrivate->window && itemToHoverPrivate->hoverEnabled) {
hoverItems.prepend(itemToHover);
sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp);
}
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
hoverEvent.setAccepted(true);
hh->handlePointerEvent(&hoverEvent);
if (hh->isHovered()) {
// Mark the whole item as updated, even if only the handler is
// actually in a hovered state (because of HoverHandler.margins)
hoverItems[item] = currentHoverId;
}
}
}
return true;
}
return false;
return accepted;
}
// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
@ -1378,12 +1477,7 @@ void QQuickDeliveryAgentPrivate::handleMouseEvent(QMouseEvent *event)
QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition;
lastMousePosition = event->scenePosition();
qCDebug(lcHoverTrace) << q << "mouse pos" << last << "->" << lastMousePosition << event;
bool accepted = event->isAccepted();
bool delivered = deliverHoverEvent(rootItem, event->scenePosition(), last, event->modifiers(), event->timestamp(), accepted);
if (!delivered) {
//take care of any exits
accepted = clearHover(event->timestamp());
}
bool accepted = deliverHoverEvent(event->scenePosition(), last, event->modifiers(), event->timestamp());
event->setAccepted(accepted);
}
deliverPointerEvent(event);
@ -1422,11 +1516,8 @@ void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
// TODO do this for each known mouse device or come up with a different strategy
if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
!lastMousePosition.isNull() && QQuickWindowPrivate::get(win)->dirtyItemList) {
bool accepted = false;
qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root";
bool delivered = deliverHoverEvent(rootItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
if (!delivered)
clearHover(); // take care of any exits
deliverHoverEvent(lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0);
qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
}
#endif

View File

@ -105,13 +105,14 @@ public:
#endif
QQuickItem *lastUngrabbed = nullptr;
QStack<QPointerEvent *> eventsInDelivery;
QList<QPointer<QQuickItem>> hoverItems;
QFlatMap<QPointer<QQuickItem>, uint> hoverItems;
QVector<QQuickItem *> hasFiltered; // during event delivery to a single receiver, the filtering parents for which childMouseEventFilter was already called
QVector<QQuickItem *> skipDelivery; // during delivery of one event to all receivers, Items to which we know delivery is no longer necessary
QScopedPointer<QMutableTouchEvent> delayedTouch;
QList<const QPointingDevice *> knownPointingDevices;
uint currentHoverId = 0;
#if QT_CONFIG(wheelevent)
uint lastWheelEventAccepted = 0;
#endif
@ -189,7 +190,9 @@ public:
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
bool deliverHoverEvent(const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp);
bool deliverHoverEventRecursive(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp);
bool deliverHoverEventToItem(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp);
bool clearHover(ulong timestamp = 0);

View File

@ -190,8 +190,8 @@ void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler()
#endif
QTest::mouseMove(window, buttonCenter);
QCOMPARE(topSidebarHH->isHovered(), true);
QCOMPARE(sidebarHoveredSpy.count(), 1);
QCOMPARE(topSidebarHH->isHovered(), false);
QCOMPARE(sidebarHoveredSpy.count(), 2);
QCOMPARE(buttonMA->hovered(), true);
QCOMPARE(buttonHoveredSpy.count(), 1);
#if QT_CONFIG(cursor)
@ -200,7 +200,7 @@ void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler()
QTest::mouseMove(window, rightOfButton);
QCOMPARE(topSidebarHH->isHovered(), true);
QCOMPARE(sidebarHoveredSpy.count(), 1);
QCOMPARE(sidebarHoveredSpy.count(), 3);
QCOMPARE(buttonMA->hovered(), false);
QCOMPARE(buttonHoveredSpy.count(), 2);
#if QT_CONFIG(cursor)
@ -209,7 +209,7 @@ void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler()
QTest::mouseMove(window, outOfSidebar);
QCOMPARE(topSidebarHH->isHovered(), false);
QCOMPARE(sidebarHoveredSpy.count(), 2);
QCOMPARE(sidebarHoveredSpy.count(), 4);
QCOMPARE(buttonMA->hovered(), false);
QCOMPARE(buttonHoveredSpy.count(), 2);
#if QT_CONFIG(cursor)
@ -256,8 +256,8 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea()
#endif
QTest::mouseMove(window, buttonCenter);
QCOMPARE(bottomSidebarMA->hovered(), false);
QCOMPARE(sidebarHoveredSpy.count(), 2);
QCOMPARE(bottomSidebarMA->hovered(), true);
QCOMPARE(sidebarHoveredSpy.count(), 1);
QCOMPARE(buttonHH->isHovered(), true);
QCOMPARE(buttonHoveredSpy.count(), 1);
#if QT_CONFIG(cursor)
@ -266,7 +266,7 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea()
QTest::mouseMove(window, rightOfButton);
QCOMPARE(bottomSidebarMA->hovered(), true);
QCOMPARE(sidebarHoveredSpy.count(), 3);
QCOMPARE(sidebarHoveredSpy.count(), 1);
QCOMPARE(buttonHH->isHovered(), false);
QCOMPARE(buttonHoveredSpy.count(), 2);
#if QT_CONFIG(cursor)
@ -275,7 +275,7 @@ void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea()
QTest::mouseMove(window, outOfSidebar);
QCOMPARE(bottomSidebarMA->hovered(), false);
QCOMPARE(sidebarHoveredSpy.count(), 4);
QCOMPARE(sidebarHoveredSpy.count(), 2);
QCOMPARE(buttonHH->isHovered(), false);
QCOMPARE(buttonHoveredSpy.count(), 2);
#if QT_CONFIG(cursor)