QWidgetWindow: fix enter/leave events not sent due to fractional scaling

If widget A starts at y=0 and widget B starts at y=19, when the mouse
moves from y=15 to y=18.6667, the code was doing childAt(p) with a
rounded QPoint y=19 and finding B, but then the relative coord for B
was set to -0.33333 so B wasn't under the mouse and didn't get an enter
event (and since it's now the "last receiver", it doesn't get one later
either when y is bigger).

Add QWidget::childAt(QPointF) to fix this.

Fixes: QTBUG-128391
Change-Id: I76d4b711a8297648780bdd079eb67405ab12be14
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit 40bde8a572bd8ed039d3f5a5ab99b281de7410bd)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
David Faure 2024-08-27 19:41:53 +02:00 committed by Qt Cherry-pick Bot
parent 5ebab802cc
commit cd528d81f7
6 changed files with 38 additions and 21 deletions

View File

@ -2253,8 +2253,8 @@ bool QApplicationPrivate::modalState()
/*
\internal
*/
QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint &windowPos,
QPoint *pos, QEvent::Type type,
QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPointF &windowPos,
QPointF *pos, QEvent::Type type,
Qt::MouseButtons buttons, QWidget *buttonDown,
QWidget *alienWidget)
{

View File

@ -168,7 +168,7 @@ public:
static QString styleSheet;
#endif
static QPointer<QWidget> leaveAfterRelease;
static QWidget *pickMouseReceiver(QWidget *candidate, const QPoint &windowPos, QPoint *pos,
static QWidget *pickMouseReceiver(QWidget *candidate, const QPointF &windowPos, QPointF *pos,
QEvent::Type type, Qt::MouseButtons buttons,
QWidget *buttonDown, QWidget *alienWidget);
static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget,

View File

@ -10449,11 +10449,24 @@ bool QWidget::hasHeightForWidth() const
*/
QWidget *QWidget::childAt(const QPoint &p) const
{
return d_func()->childAt_helper(QPointF(p), false);
}
/*!
\overload
\since 6.8
Returns the visible child widget at point \a p in the widget's own
coordinate system.
*/
QWidget *QWidget::childAt(const QPointF &p) const
{
return d_func()->childAt_helper(p, false);
}
QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDestructor) const
QWidget *QWidgetPrivate::childAt_helper(const QPointF &p, bool ignoreChildrenInDestructor) const
{
if (children.isEmpty())
return nullptr;
@ -10463,7 +10476,7 @@ QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDe
return childAtRecursiveHelper(p, ignoreChildrenInDestructor);
}
QWidget *QWidgetPrivate::childAtRecursiveHelper(const QPoint &p, bool ignoreChildrenInDestructor) const
QWidget *QWidgetPrivate::childAtRecursiveHelper(const QPointF &p, bool ignoreChildrenInDestructor) const
{
for (int i = children.size() - 1; i >= 0; --i) {
QWidget *child = qobject_cast<QWidget *>(children.at(i));
@ -10473,7 +10486,7 @@ QWidget *QWidgetPrivate::childAtRecursiveHelper(const QPoint &p, bool ignoreChil
}
// Map the point 'p' from parent coordinates to child coordinates.
QPoint childPoint = p;
QPointF childPoint = p;
childPoint -= child->data->crect.topLeft();
// Check if the point hits the child.

View File

@ -624,6 +624,7 @@ public:
static QWidget *find(WId);
inline QWidget *childAt(int x, int y) const;
QWidget *childAt(const QPoint &p) const;
QWidget *childAt(const QPointF &p) const;
void setAttribute(Qt::WidgetAttribute, bool on = true);
inline bool testAttribute(Qt::WidgetAttribute) const;
@ -796,7 +797,7 @@ template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o)
#endif // !Q_QDOC
inline QWidget *QWidget::childAt(int ax, int ay) const
{ return childAt(QPoint(ax, ay)); }
{ return childAt(QPointF(ax, ay)); }
inline Qt::WindowType QWidget::windowType() const
{ return static_cast<Qt::WindowType>((data->window_flags & Qt::WindowType_Mask).toInt()); }

View File

@ -420,9 +420,9 @@ public:
bool setMinimumSize_helper(int &minw, int &minh);
bool setMaximumSize_helper(int &maxw, int &maxh);
void setConstraints_sys();
bool pointInsideRectAndMask(const QPoint &) const;
QWidget *childAt_helper(const QPoint &, bool) const;
QWidget *childAtRecursiveHelper(const QPoint &p, bool) const;
bool pointInsideRectAndMask(const QPointF &) const;
QWidget *childAt_helper(const QPointF &, bool) const;
QWidget *childAtRecursiveHelper(const QPointF &p, bool) const;
void updateGeometry_helper(bool forceUpdate);
void getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const;
@ -884,11 +884,14 @@ inline void QWidgetPrivate::setSharedPainter(QPainter *painter)
x->sharedPainter = painter;
}
inline bool QWidgetPrivate::pointInsideRectAndMask(const QPoint &p) const
inline bool QWidgetPrivate::pointInsideRectAndMask(const QPointF &p) const
{
Q_Q(const QWidget);
return q->rect().contains(p) && (!extra || !extra->hasMask || q->testAttribute(Qt::WA_MouseNoMask)
|| extra->mask.contains(p));
// Use QRectF::contains so that (0, -0.1) isn't in, with p.toPoint() it would be
// The adjusted matches QRect semantics: (160,160) isn't contained in QRect(0, 0, 160, 160)
return QRectF(q->rect().adjusted(0, 0, -1, -1)).contains(p)
&& (!extra || !extra->hasMask || q->testAttribute(Qt::WA_MouseNoMask)
|| extra->mask.contains(p.toPoint() /* incorrect for the -0.1 case */));
}
inline QWidgetRepaintManager *QWidgetPrivate::maybeRepaintManager() const

View File

@ -450,7 +450,7 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event)
}
} else {
const QEnterEvent *ee = static_cast<QEnterEvent *>(event);
QWidget *child = m_widget->childAt(ee->position().toPoint());
QWidget *child = m_widget->childAt(ee->position());
QWidget *receiver = child ? child : m_widget.data();
QWidget *leave = nullptr;
if (QApplicationPrivate::inPopupMode() && receiver == m_widget
@ -513,7 +513,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if (activePopupWidget != m_widget)
mapped = activePopupWidget->mapFromGlobal(event->globalPosition());
bool releaseAfter = false;
QWidget *popupChild = activePopupWidget->childAt(mapped.toPoint());
QWidget *popupChild = activePopupWidget->childAt(mapped);
if (activePopupWidget != qt_popup_down) {
qt_button_down = nullptr;
@ -601,8 +601,8 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
return;
// which child should have it?
QWidget *widget = m_widget->childAt(event->position().toPoint());
QPoint mapped = event->position().toPoint();
QWidget *widget = m_widget->childAt(event->position());
QPointF mapped = event->position();
if (!widget)
widget = m_widget;
@ -611,7 +611,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if (event->type() == QEvent::MouseButtonPress && initialPress)
qt_button_down = widget;
QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->scenePosition().toPoint(), &mapped, event->type(), event->buttons(),
QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->scenePosition(), &mapped, event->type(), event->buttons(),
qt_button_down, widget);
if (!receiver)
return;
@ -637,7 +637,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
&& event->button() == Qt::RightButton
&& m_widget->rect().contains(event->position().toPoint())) {
QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPosition().toPoint(), event->modifiers());
QContextMenuEvent e(QContextMenuEvent::Mouse, mapped.toPoint(), event->globalPosition().toPoint(), event->modifiers());
QGuiApplication::forwardEvent(receiver, &e, event);
if (e.isAccepted())
event->accept();
@ -867,7 +867,7 @@ void QWidgetWindow::handleWheelEvent(QWheelEvent *event)
}
// which child should have it?
QWidget *widget = rootWidget->childAt(pos.toPoint());
QWidget *widget = rootWidget->childAt(pos);
if (!widget)
widget = rootWidget;
@ -1076,7 +1076,7 @@ void QWidgetWindow::handleTabletEvent(QTabletEvent *event)
QWidget *widget = qt_tablet_target;
if (!widget) {
widget = m_widget->childAt(event->position().toPoint());
widget = m_widget->childAt(event->position());
if (!widget)
widget = m_widget;
if (event->type() == QEvent::TabletPress)