aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2022-10-26 14:03:30 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-11-01 09:18:09 +0000
commiteca92bea571cf691423b627d4d6270f7c2807bd9 (patch)
tree70541098ba712942836f07d21386fc166e344bda
parente27b91e4cc277698c62191ce3c349afc988a2a2b (diff)
QQuickHoverHandler: listen for parent changes, and update hasHoverInChild
A QQuickHoverHandler is normally created by the QML engine, but can sometimes also be created directly from C++. And for the latter case, QQuickHoverHandler::componentComplete() will not be called. This causes a problem, since it takes care of subscribing for hover events on the parent item. To support creating hover handlers from c++, we therefore need to also subscribe to hover events from the constructor. Moreover, since the parentItem can change at runtime, we also need a virtual function that informs it when the parent item changes, so that we can remove hover subscription from the old parent, and subscribe for it on the new parent. Change-Id: I52f3cd16d6bbfbbe2e4c3c019efdc7f06c5f2c31 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> (cherry picked from commit 7db67e59d45b47e45a2723808f23e8cdadf5065c) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp51
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp5
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h1
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml15
-rw-r--r--tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp64
5 files changed, 123 insertions, 13 deletions
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index 2a2208c567..072ca626a6 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -40,35 +40,58 @@ class QQuickHoverHandlerPrivate : public QQuickSinglePointHandlerPrivate
public:
void onEnabledChanged() override;
+ void onParentChanged(QQuickItem *oldParent, QQuickItem *newParent) override;
+
+ void updateHasHoverInChild(QQuickItem *item, bool hasHover);
};
void QQuickHoverHandlerPrivate::onEnabledChanged()
{
Q_Q(QQuickHoverHandler);
- if (auto parent = q->parentItem()) {
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
- itemPriv->setHasHoverInChild(enabled);
- // The DA needs to resolve which items and handlers should now be hovered or unhovered.
- // Marking the parent item dirty ensures that flushFrameSynchronousEvents() will be called from the render loop,
- // even if this change is not in response to a mouse event and no item has already marked itself dirty.
- itemPriv->dirty(QQuickItemPrivate::Content);
- }
+ if (auto parent = q->parentItem())
+ updateHasHoverInChild(parent, enabled);
if (!enabled)
q->setHovered(false);
+
+ QQuickSinglePointHandlerPrivate::onEnabledChanged();
+}
+
+void QQuickHoverHandlerPrivate::onParentChanged(QQuickItem *oldParent, QQuickItem *newParent)
+{
+ if (oldParent)
+ updateHasHoverInChild(oldParent, false);
+ if (newParent)
+ updateHasHoverInChild(newParent, true);
+
+ QQuickSinglePointHandlerPrivate::onParentChanged(oldParent, newParent);
+}
+
+void QQuickHoverHandlerPrivate::updateHasHoverInChild(QQuickItem *item, bool hasHover)
+{
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
+ itemPriv->setHasHoverInChild(hasHover);
+ // The DA needs to resolve which items and handlers should now be hovered or unhovered.
+ // Marking the parent item dirty ensures that flushFrameSynchronousEvents() will be called from the render loop,
+ // even if this change is not in response to a mouse event and no item has already marked itself dirty.
+ itemPriv->dirty(QQuickItemPrivate::Content);
}
QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
: QQuickSinglePointHandler(*(new QQuickHoverHandlerPrivate), parent)
{
+ Q_D(QQuickHoverHandler);
// Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
- d_func()->acceptedButtons = Qt::NoButton;
+ d->acceptedButtons = Qt::NoButton;
+ if (parent)
+ d->updateHasHoverInChild(parent, true);
}
QQuickHoverHandler::~QQuickHoverHandler()
{
+ Q_D(QQuickHoverHandler);
if (auto parent = parentItem())
- QQuickItemPrivate::get(parent)->setHasHoverInChild(false);
+ d->updateHasHoverInChild(parent, false);
}
/*!
@@ -105,9 +128,13 @@ bool QQuickHoverHandler::event(QEvent *event)
void QQuickHoverHandler::componentComplete()
{
+ Q_D(QQuickHoverHandler);
QQuickSinglePointHandler::componentComplete();
- if (auto par = parentItem())
- QQuickItemPrivate::get(par)->setHasHoverInChild(true);
+
+ if (d->enabled) {
+ if (auto parent = parentItem())
+ d->updateHasHoverInChild(parent, true);
+ }
}
bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event)
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 96f87d6943..a851334b0e 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -597,15 +597,18 @@ QQuickItem *QQuickPointerHandler::parentItem() const
void QQuickPointerHandler::setParentItem(QQuickItem *p)
{
+ Q_D(QQuickPointerHandler);
if (QObject::parent() == p)
return;
qCDebug(lcHandlerParent) << "reparenting handler" << this << ":" << parent() << "->" << p;
- if (auto *oldParent = static_cast<QQuickItem *>(QObject::parent()))
+ auto *oldParent = static_cast<QQuickItem *>(QObject::parent());
+ if (oldParent)
QQuickItemPrivate::get(oldParent)->removePointerHandler(this);
setParent(p);
if (p)
QQuickItemPrivate::get(p)->addPointerHandler(this);
+ d->onParentChanged(oldParent, p);
emit parentChanged();
}
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
index fa4ab737db..aa09f4edb7 100644
--- a/src/quick/handlers/qquickpointerhandler_p_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -38,6 +38,7 @@ public:
bool dragOverThreshold(QVector2D delta) const;
bool dragOverThreshold(const QEventPoint &point) const;
+ virtual void onParentChanged(QQuickItem * /*oldParent*/, QQuickItem * /*newParent*/) {}
virtual void onEnabledChanged() {}
static QVector<QObject *> &deviceDeliveryTargets(const QInputDevice *device);
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml
new file mode 100644
index 0000000000..ed3728e278
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/nohandler.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.15
+import QtQuick.Window 2.15
+
+Window {
+ width: 320
+ height: 240
+ visible: true
+
+ Rectangle {
+ objectName: "childItem"
+ width: 100
+ height: 100
+ color: "lightblue"
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
index b17395a99d..be308cfa64 100644
--- a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp
@@ -46,6 +46,7 @@ private slots:
void window();
void deviceCursor_data();
void deviceCursor();
+ void addHandlerFromCpp();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -567,6 +568,69 @@ void tst_HoverHandler::deviceCursor()
QCOMPARE(airbrushEraserHandler->isHovered(), true); // there was no fresh QTabletEvent to tell it not to be hovered
}
+void tst_HoverHandler::addHandlerFromCpp()
+{
+ // Check that you can create a hover handler from c++, and add it
+ // as a child of an existing item. Continue to check that you can
+ // also change the parent item at runtime.
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("nohandler.qml"));
+ QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create()));
+ QVERIFY(!window.isNull());
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickItem *childItem = window->findChild<QQuickItem *>("childItem");
+ QVERIFY(childItem);
+
+ // Move mouse outside child
+ const QPoint outside(200, 200);
+ const QPoint inside(50, 50);
+ QTest::mouseMove(window.data(), outside);
+
+ QQuickHoverHandler *handler = new QQuickHoverHandler(childItem);
+ QSignalSpy spy(handler, &QQuickHoverHandler::hoveredChanged);
+
+ // Move mouse inside child
+ QTest::mouseMove(window.data(), inside);
+ QVERIFY(handler->isHovered());
+ QCOMPARE(spy.count(), 1);
+
+ // Move mouse outside child
+ QTest::mouseMove(window.data(), outside);
+ QVERIFY(!handler->isHovered());
+ QCOMPARE(spy.count(), 2);
+
+ // Remove the parent item from the handler
+ spy.clear();
+ handler->setParentItem(nullptr);
+
+ // Move mouse inside child
+ QTest::mouseMove(window.data(), inside);
+ QVERIFY(!handler->isHovered());
+ QCOMPARE(spy.count(), 0);
+
+ // Move mouse outside child
+ QTest::mouseMove(window.data(), outside);
+ QVERIFY(!handler->isHovered());
+ QCOMPARE(spy.count(), 0);
+
+ // Reparent back the item to the handler
+ spy.clear();
+ handler->setParentItem(childItem);
+
+ // Move mouse inside child
+ QTest::mouseMove(window.data(), inside);
+ QVERIFY(handler->isHovered());
+ QCOMPARE(spy.count(), 1);
+
+ // Move mouse outside child
+ QTest::mouseMove(window.data(), outside);
+ QVERIFY(!handler->isHovered());
+ QCOMPARE(spy.count(), 2);
+}
+
QTEST_MAIN(tst_HoverHandler)
#include "tst_qquickhoverhandler.moc"