QApplication: send QHoverEvents with correct scenePosition()
The QHoverEvent ctor takes two points: pos and globalPos; pos is then passed as both the scene and global pos to the QSinglePointEvent ctor, which calls QMutableEventPoint::setScenePosition() on the persistent QEventPoint instance and then detaches befeore setting ephemeral state. Therefore, we must construct QHoverEvent with scene position first, not local position, so that the right value is persisted; it's better to set local position after the detach(), whereas it's too late to fix the persistent point then. Fixes: QTBUG-106918 Change-Id: I45726a9ec05bba2fe0cde6f5fb87c269105caca6 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> (cherry picked from commit c3e2a624fbaa63979eecfc0b9346f4b54cb65264) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
93aa02e671
commit
a1dd185539
@ -2079,8 +2079,9 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave, con
|
|||||||
QCoreApplication::sendEvent(w, &enterEvent);
|
QCoreApplication::sendEvent(w, &enterEvent);
|
||||||
if (w->testAttribute(Qt::WA_Hover) &&
|
if (w->testAttribute(Qt::WA_Hover) &&
|
||||||
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) {
|
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) {
|
||||||
QHoverEvent he(QEvent::HoverEnter, localPos, QPointF(-1, -1), globalPos,
|
QHoverEvent he(QEvent::HoverEnter, windowPos, QPointF(-1, -1), globalPos,
|
||||||
QGuiApplication::keyboardModifiers());
|
QGuiApplication::keyboardModifiers());
|
||||||
|
QMutableEventPoint::setPosition(he.point(0), localPos);
|
||||||
qApp->d_func()->notify_helper(w, &he);
|
qApp->d_func()->notify_helper(w, &he);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2823,7 +2824,8 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
|
|||||||
while (w) {
|
while (w) {
|
||||||
if (w->testAttribute(Qt::WA_Hover) &&
|
if (w->testAttribute(Qt::WA_Hover) &&
|
||||||
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) {
|
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == w->window())) {
|
||||||
QHoverEvent he(QEvent::HoverMove, relpos, mouse->globalPosition(), relpos - diff, mouse->modifiers());
|
QHoverEvent he(QEvent::HoverMove, mouse->scenePosition(), mouse->globalPosition(), relpos - diff, mouse->modifiers());
|
||||||
|
QMutableEventPoint::setPosition(he.point(0), relpos);
|
||||||
d->notify_helper(w, &he);
|
d->notify_helper(w, &he);
|
||||||
}
|
}
|
||||||
if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
|
if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
|
||||||
|
@ -345,6 +345,7 @@ private slots:
|
|||||||
void enterLeaveOnWindowShowHide_data();
|
void enterLeaveOnWindowShowHide_data();
|
||||||
void enterLeaveOnWindowShowHide();
|
void enterLeaveOnWindowShowHide();
|
||||||
void taskQTBUG_4055_sendSyntheticEnterLeave();
|
void taskQTBUG_4055_sendSyntheticEnterLeave();
|
||||||
|
void hoverPosition();
|
||||||
void underMouse();
|
void underMouse();
|
||||||
void taskQTBUG_27643_enterEvents();
|
void taskQTBUG_27643_enterEvents();
|
||||||
#endif
|
#endif
|
||||||
@ -10390,6 +10391,106 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave()
|
|||||||
QTRY_COMPARE(child.numEnterEvents, 1);
|
QTRY_COMPARE(child.numEnterEvents, 1);
|
||||||
QCOMPARE(child.numMouseMoveEvents, 0);
|
QCOMPARE(child.numMouseMoveEvents, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QWidget::hoverPosition()
|
||||||
|
{
|
||||||
|
if (m_platform == QStringLiteral("wayland"))
|
||||||
|
QSKIP("Wayland: Clients can't set cursor position on wayland.");
|
||||||
|
|
||||||
|
class HoverWidget : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HoverWidget(QWidget *parent = nullptr) : QWidget(parent) {
|
||||||
|
setMouseTracking(true);
|
||||||
|
setAttribute(Qt::WA_Hover);
|
||||||
|
}
|
||||||
|
bool event(QEvent *ev) override {
|
||||||
|
switch (ev->type()) {
|
||||||
|
case QEvent::HoverMove:
|
||||||
|
// The docs say that WA_Hover will cause a paint event on enter and leave, but not on move.
|
||||||
|
update();
|
||||||
|
Q_FALLTHROUGH();
|
||||||
|
case QEvent::HoverEnter:
|
||||||
|
case QEvent::HoverLeave: {
|
||||||
|
qCDebug(lcTests) << ev;
|
||||||
|
lastHoverType = ev->type();
|
||||||
|
++hoverEventCount;
|
||||||
|
QHoverEvent *hov = static_cast<QHoverEvent *>(ev);
|
||||||
|
mousePos = hov->position().toPoint();
|
||||||
|
mouseScenePos = hov->scenePosition().toPoint();
|
||||||
|
if (ev->type() == QEvent::HoverEnter)
|
||||||
|
mouseEnterScenePos = hov->scenePosition().toPoint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QWidget::event(ev);
|
||||||
|
}
|
||||||
|
void paintEvent(QPaintEvent *) override {
|
||||||
|
++paintEventCount;
|
||||||
|
QPainter painter(this);
|
||||||
|
if (mousePos.x() > 0)
|
||||||
|
painter.setPen(Qt::red);
|
||||||
|
painter.drawRect(0, 0, width(), height());
|
||||||
|
painter.setPen(Qt::darkGreen);
|
||||||
|
painter.drawLine(mousePos - QPoint(crossHalfWidth, 0), mousePos + QPoint(crossHalfWidth, 0));
|
||||||
|
painter.drawLine(mousePos - QPoint(0, crossHalfWidth), mousePos + QPoint(0, crossHalfWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
QEvent::Type lastHoverType = QEvent::None;
|
||||||
|
int hoverEventCount = 0;
|
||||||
|
int paintEventCount = 0;
|
||||||
|
QPoint mousePos;
|
||||||
|
QPoint mouseScenePos;
|
||||||
|
QPoint mouseEnterScenePos;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int crossHalfWidth = 5;
|
||||||
|
};
|
||||||
|
|
||||||
|
QCursor::setPos(m_safeCursorPos);
|
||||||
|
if (!QTest::qWaitFor([this]{ return QCursor::pos() == m_safeCursorPos; }))
|
||||||
|
QSKIP("Can't move cursor");
|
||||||
|
|
||||||
|
QWidget root;
|
||||||
|
root.resize(300, 300);
|
||||||
|
HoverWidget h(&root);
|
||||||
|
h.setGeometry(100, 100, 100, 100);
|
||||||
|
root.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||||||
|
|
||||||
|
const QPoint middle(50, 50);
|
||||||
|
QPoint curpos = h.mapToGlobal(middle);
|
||||||
|
QCursor::setPos(curpos);
|
||||||
|
if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
|
||||||
|
QSKIP("Can't move cursor");
|
||||||
|
QTRY_COMPARE_GE(h.hoverEventCount, 1); // HoverEnter and then probably HoverMove, so usually 2
|
||||||
|
QTRY_COMPARE_GE(h.paintEventCount, 2);
|
||||||
|
const int enterHoverEventCount = h.hoverEventCount;
|
||||||
|
qCDebug(lcTests) << "hover enter events:" << enterHoverEventCount << "last was" << h.lastHoverType
|
||||||
|
<< "; paint events:" << h.paintEventCount;
|
||||||
|
QCOMPARE(h.mousePos, middle);
|
||||||
|
QCOMPARE(h.mouseEnterScenePos, h.mapToParent(middle));
|
||||||
|
QCOMPARE(h.mouseScenePos, h.mapToParent(middle));
|
||||||
|
QCOMPARE(h.lastHoverType, enterHoverEventCount == 1 ? QEvent::HoverEnter : QEvent::HoverMove);
|
||||||
|
|
||||||
|
curpos += {10, 10};
|
||||||
|
QCursor::setPos(curpos);
|
||||||
|
if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
|
||||||
|
QSKIP("Can't move cursor");
|
||||||
|
QTRY_COMPARE(h.hoverEventCount, enterHoverEventCount + 1);
|
||||||
|
QCOMPARE(h.lastHoverType, QEvent::HoverMove);
|
||||||
|
QTRY_COMPARE_GE(h.paintEventCount, 3);
|
||||||
|
|
||||||
|
curpos += {50, 50}; // in the outer widget, but leaving the inner widget
|
||||||
|
QCursor::setPos(curpos);
|
||||||
|
if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
|
||||||
|
QSKIP("Can't move cursor");
|
||||||
|
QTRY_COMPARE(h.lastHoverType, QEvent::HoverLeave);
|
||||||
|
QCOMPARE_GE(h.hoverEventCount, enterHoverEventCount + 2);
|
||||||
|
QTRY_COMPARE_GE(h.paintEventCount, 4);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void tst_QWidget::windowFlags()
|
void tst_QWidget::windowFlags()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user