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 \internal
*/ */
QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPoint &windowPos, QWidget *QApplicationPrivate::pickMouseReceiver(QWidget *candidate, const QPointF &windowPos,
QPoint *pos, QEvent::Type type, QPointF *pos, QEvent::Type type,
Qt::MouseButtons buttons, QWidget *buttonDown, Qt::MouseButtons buttons, QWidget *buttonDown,
QWidget *alienWidget) QWidget *alienWidget)
{ {

View File

@ -168,7 +168,7 @@ public:
static QString styleSheet; static QString styleSheet;
#endif #endif
static QPointer<QWidget> leaveAfterRelease; 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, QEvent::Type type, Qt::MouseButtons buttons,
QWidget *buttonDown, QWidget *alienWidget); QWidget *buttonDown, QWidget *alienWidget);
static bool sendMouseEvent(QWidget *receiver, QMouseEvent *event, 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 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); 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()) if (children.isEmpty())
return nullptr; return nullptr;
@ -10463,7 +10476,7 @@ QWidget *QWidgetPrivate::childAt_helper(const QPoint &p, bool ignoreChildrenInDe
return childAtRecursiveHelper(p, ignoreChildrenInDestructor); 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) { for (int i = children.size() - 1; i >= 0; --i) {
QWidget *child = qobject_cast<QWidget *>(children.at(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. // Map the point 'p' from parent coordinates to child coordinates.
QPoint childPoint = p; QPointF childPoint = p;
childPoint -= child->data->crect.topLeft(); childPoint -= child->data->crect.topLeft();
// Check if the point hits the child. // Check if the point hits the child.

View File

@ -624,6 +624,7 @@ public:
static QWidget *find(WId); static QWidget *find(WId);
inline QWidget *childAt(int x, int y) const; inline QWidget *childAt(int x, int y) const;
QWidget *childAt(const QPoint &p) const; QWidget *childAt(const QPoint &p) const;
QWidget *childAt(const QPointF &p) const;
void setAttribute(Qt::WidgetAttribute, bool on = true); void setAttribute(Qt::WidgetAttribute, bool on = true);
inline bool testAttribute(Qt::WidgetAttribute) const; inline bool testAttribute(Qt::WidgetAttribute) const;
@ -796,7 +797,7 @@ template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o)
#endif // !Q_QDOC #endif // !Q_QDOC
inline QWidget *QWidget::childAt(int ax, int ay) const 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 inline Qt::WindowType QWidget::windowType() const
{ return static_cast<Qt::WindowType>((data->window_flags & Qt::WindowType_Mask).toInt()); } { 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 setMinimumSize_helper(int &minw, int &minh);
bool setMaximumSize_helper(int &maxw, int &maxh); bool setMaximumSize_helper(int &maxw, int &maxh);
void setConstraints_sys(); void setConstraints_sys();
bool pointInsideRectAndMask(const QPoint &) const; bool pointInsideRectAndMask(const QPointF &) const;
QWidget *childAt_helper(const QPoint &, bool) const; QWidget *childAt_helper(const QPointF &, bool) const;
QWidget *childAtRecursiveHelper(const QPoint &p, bool) const; QWidget *childAtRecursiveHelper(const QPointF &p, bool) const;
void updateGeometry_helper(bool forceUpdate); void updateGeometry_helper(bool forceUpdate);
void getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const; void getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const;
@ -884,11 +884,14 @@ inline void QWidgetPrivate::setSharedPainter(QPainter *painter)
x->sharedPainter = painter; x->sharedPainter = painter;
} }
inline bool QWidgetPrivate::pointInsideRectAndMask(const QPoint &p) const inline bool QWidgetPrivate::pointInsideRectAndMask(const QPointF &p) const
{ {
Q_Q(const QWidget); Q_Q(const QWidget);
return q->rect().contains(p) && (!extra || !extra->hasMask || q->testAttribute(Qt::WA_MouseNoMask) // Use QRectF::contains so that (0, -0.1) isn't in, with p.toPoint() it would be
|| extra->mask.contains(p)); // 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 inline QWidgetRepaintManager *QWidgetPrivate::maybeRepaintManager() const

View File

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