From 189f9873ae3f23377708fbf9880398fd6a078715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 13 Nov 2023 16:49:25 +0100 Subject: [PATCH] macOS: Take window mask into account when computing QCocoaScreen::topLevelAt Although not explicitly documented, this is the behavior in practice on XCB and Windows, and we rely on this behavior in our implementation of QApplication::widgetAt(), where we punch a temporary hole in the widget using a mask if it has Qt::WA_TransparentForMouseEvents set. Pick-to: 6.5 6.6 Fixes: QTBUG-41696 Task-number: QTBUG-119092 Change-Id: Ie7abc31b6930ee6b56fcdf391befc625c1ddf502 Reviewed-by: Timur Pocheptsov Reviewed-by: Qt CI Bot --- src/plugins/platforms/cocoa/qcocoascreen.mm | 7 +++- .../qguiapplication/tst_qguiapplication.cpp | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 7f1995761a7..966b412de33 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -542,7 +542,12 @@ QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const if (!w->isVisible()) return; - if (!QHighDpi::toNativePixels(w->geometry(), w).contains(point)) + auto nativeGeometry = QHighDpi::toNativePixels(w->geometry(), w); + if (!nativeGeometry.contains(point)) + return; + + QRegion mask = QHighDpi::toNativeLocalPosition(w->mask(), w); + if (!mask.isEmpty() && !mask.contains(point - nativeGeometry.topLeft())) return; window = w; diff --git a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp index 2a2dbd0e48e..e4f1158d27d 100644 --- a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp +++ b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp @@ -61,6 +61,8 @@ private slots: void staticFunctions(); + void topLevelAt(); + void settableStyleHints_data(); void settableStyleHints(); // Needs to run last as it changes style hints. }; @@ -1325,6 +1327,37 @@ void tst_QGuiApplication::staticFunctions() QPixmap::defaultDepth(); } +void tst_QGuiApplication::topLevelAt() +{ + int argc = 1; + char *argv[] = { const_cast("tst_qguiapplication") }; + QGuiApplication app(argc, argv); + + QWindow bottom; + bottom.setObjectName("Bottom"); + bottom.setFlag(Qt::FramelessWindowHint); + bottom.setGeometry(200, 200, 200, 200); + bottom.showNormal(); + QVERIFY(QTest::qWaitForWindowExposed(&bottom)); + QTRY_COMPARE(app.topLevelAt(QPoint(300, 300)), &bottom); + + QWindow top; + top.setObjectName("Top"); + top.setFlag(Qt::FramelessWindowHint); + top.setGeometry(200, 200, 200, 200); + top.showNormal(); + QVERIFY(QTest::qWaitForWindowExposed(&top)); + top.raise(); + QTRY_COMPARE(app.topLevelAt(QPoint(300, 300)), &top); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowMasks)) + QSKIP("QWindow::setMask() is not supported."); + + top.setMask(QRect(0, 0, 50, 50)); + QTRY_COMPARE(app.topLevelAt(QPoint(300, 300)), &bottom); + QTRY_COMPARE(app.topLevelAt(QPoint(225, 225)), &top); +} + void tst_QGuiApplication::settableStyleHints_data() { QTest::addColumn("appInstance");