Fix pointer delivery to child items of items with clip:true
QQuickDeliveryAgentPrivate::pointerTargets() can visit a lot of items and their handlers, before actual event delivery really starts. One optimization in place since6adc36115f
is that if an item is clipped, and the point is outside its bounds, we can be sure that it's also irrelevant to the item's children, because the parts of any children that may be under that point are clipped away and invisible. At the time that was written, QQuickItem::contains() was only doing a simple bounding-rect check. Since then,bf74a908cb
added containmentMask; and we should also keep in mind the precedence of the PointerHandler.margin property (currently, TapHandler.margin does not expand the sensitive area beyond a clipped Rectangle, or beyond the containmentMask either). So it seems we now need to check clipRect().contains() explicitly: a child item may be outside its parent's containmentMask, and containmentMask does not affect clipping, so one would expect to still be able to interact with the child. The current definition of clipRect() is from9b62f4c27a
. It's virtual, but documented as "the region intended to remain visible if clip is true". Fixes: QTBUG-115179 Change-Id: I6ae8f492b99725459cdff2a89ac8508da5167102 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> (cherry picked from commitf33146ed0a
) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
0b942a8d87
commit
cf71d02152
|
@ -1889,7 +1889,7 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
|
|||
qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
|
||||
// if the item clips, we can potentially return early
|
||||
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
|
||||
if (!relevant)
|
||||
if (!item->clipRect().contains(itemPos))
|
||||
return targets;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import QtQuick
|
||||
import Qt.test 1.0
|
||||
|
||||
Item {
|
||||
width: 200
|
||||
height: 200
|
||||
|
||||
Rectangle {
|
||||
id: circle
|
||||
y: 0
|
||||
width: 100
|
||||
height: width
|
||||
radius: width/2
|
||||
color: "#3e1"
|
||||
clip: true
|
||||
|
||||
// Rectangle contains() is not affected by its 'radius' property
|
||||
containmentMask: QtObject {
|
||||
property alias radius: circle.radius
|
||||
function contains(point: point) : bool {
|
||||
return (Math.pow(point.x - radius, 2) + Math.pow(point.y - radius, 2)) < Math.pow(radius, 2)
|
||||
}
|
||||
}
|
||||
EventHandler {
|
||||
objectName: "circle eventHandler"
|
||||
}
|
||||
Rectangle {
|
||||
width: circle.width/2
|
||||
height: width
|
||||
color: "red"
|
||||
EventHandler {
|
||||
objectName: "eventHandler"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -233,6 +233,7 @@ private slots:
|
|||
void reparenting();
|
||||
void grabberSceneChange_data();
|
||||
void grabberSceneChange();
|
||||
void clip();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *, QEvent *event) override
|
||||
|
@ -800,6 +801,63 @@ void tst_PointerHandlers::grabberSceneChange()
|
|||
QTest::mouseMove(window, p1 + QPoint(5, 5));
|
||||
}
|
||||
|
||||
void tst_PointerHandlers::clip()
|
||||
{
|
||||
QScopedPointer<QQuickView> windowPtr;
|
||||
createView(windowPtr, "clip.qml");
|
||||
QQuickView * window = windowPtr.data();
|
||||
QVERIFY(window);
|
||||
window->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
|
||||
EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler");
|
||||
EventHandler *circleHandler = window->contentItem()->findChild<EventHandler*>("circle eventHandler");
|
||||
|
||||
QCOMPARE(handler->pressEventCount, 0);
|
||||
QCOMPARE(circleHandler->pressEventCount, 0);
|
||||
QCOMPARE(handler->releaseEventCount, 0);
|
||||
QCOMPARE(circleHandler->releaseEventCount, 0);
|
||||
|
||||
const QPoint rectPt = QPoint(1, 1);
|
||||
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectPt);
|
||||
QCOMPARE(handler->pressEventCount, 1);
|
||||
QCOMPARE(circleHandler->pressEventCount, 0);
|
||||
|
||||
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectPt);
|
||||
QCOMPARE(handler->releaseEventCount, 1);
|
||||
QCOMPARE(circleHandler->releaseEventCount, 0);
|
||||
|
||||
|
||||
handler->pressEventCount = 0;
|
||||
circleHandler->pressEventCount = 0;
|
||||
handler->releaseEventCount = 0;
|
||||
circleHandler->releaseEventCount = 0;
|
||||
|
||||
const QPoint rectAndCirclePt = QPoint(49 ,49);
|
||||
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
|
||||
QCOMPARE(handler->pressEventCount, 1);
|
||||
QCOMPARE(circleHandler->pressEventCount, 1);
|
||||
|
||||
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, rectAndCirclePt);
|
||||
QCOMPARE(handler->releaseEventCount, 1);
|
||||
QCOMPARE(circleHandler->releaseEventCount, 1);
|
||||
|
||||
|
||||
handler->pressEventCount = 0;
|
||||
circleHandler->pressEventCount = 0;
|
||||
handler->releaseEventCount = 0;
|
||||
circleHandler->releaseEventCount = 0;
|
||||
|
||||
const QPoint circlePt = QPoint(51 ,51);
|
||||
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, circlePt);
|
||||
QCOMPARE(handler->pressEventCount, 0);
|
||||
QCOMPARE(circleHandler->pressEventCount, 1);
|
||||
|
||||
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, circlePt);
|
||||
QCOMPARE(handler->releaseEventCount, 0);
|
||||
QCOMPARE(circleHandler->releaseEventCount, 1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_PointerHandlers)
|
||||
|
||||
#include "tst_qquickpointerhandler.moc"
|
||||
|
|
Loading…
Reference in New Issue