summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Faure <[email protected]>2024-08-27 19:41:53 +0200
committerDavid Faure <[email protected]>2024-08-30 18:39:36 +0200
commit40bde8a572bd8ed039d3f5a5ab99b281de7410bd (patch)
treed40d767b6238bfa36f2062dd283d2a7980729a8a
parent2a10a679a17de2f2ec83f676198936fcaaaa8fad (diff)
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 Pick-to: 6.8 Change-Id: I76d4b711a8297648780bdd079eb67405ab12be14 Reviewed-by: Volker Hilsheimer <[email protected]>
-rw-r--r--src/widgets/kernel/qapplication.cpp4
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidget.cpp19
-rw-r--r--src/widgets/kernel/qwidget.h3
-rw-r--r--src/widgets/kernel/qwidget_p.h15
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp16
6 files changed, 38 insertions, 21 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 5e1d7923197..89d451589a8 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -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)
{
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index 7de9f54b587..5f1d4ef13c3 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -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,
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 8b9513e2dd3..dcf53f0e84d 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -10453,10 +10453,23 @@ 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;
@@ -10466,7 +10479,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));
@@ -10476,7 +10489,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.
diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h
index 7ee4412d881..faf2b31a832 100644
--- a/src/widgets/kernel/qwidget.h
+++ b/src/widgets/kernel/qwidget.h
@@ -627,6 +627,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;
@@ -799,7 +800,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()); }
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index 7394bcdbbf8..f8ac13ebb74 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -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;
@@ -885,11 +885,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
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 46871d0da16..e2f1776780d 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -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)