Update cursor if frame-synchronous hover update discovers hover change
Also mark cursor as dirty and force update after shape change. Done-with: Matthias Rauter <matthias.rauter@qt.io> Fixes: QTBUG-53987 Fixes: QTBUG-90457 Task-number: QTBUG-54019 Change-Id: I64d9f5d0a39dbf141a8e82bee824b47a8884139b Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commitcd7c5f94a0
) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit4271d4586b
)
This commit is contained in:
parent
821aa3dcc2
commit
66610bcb57
|
@ -182,11 +182,13 @@ void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape)
|
|||
return;
|
||||
d->cursorShape = shape;
|
||||
d->cursorSet = true;
|
||||
d->cursorDirty = true;
|
||||
if (auto *parent = parentItem()) {
|
||||
QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
|
||||
itemPriv->hasCursorHandler = true;
|
||||
itemPriv->setHasCursorInChild(true);
|
||||
}
|
||||
|
||||
emit cursorShapeChanged();
|
||||
}
|
||||
|
||||
|
@ -765,6 +767,7 @@ QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
|
|||
, hadKeepMouseGrab(false)
|
||||
, hadKeepTouchGrab(false)
|
||||
, cursorSet(false)
|
||||
, cursorDirty(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
bool hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
|
||||
bool hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
|
||||
bool cursorSet : 1;
|
||||
bool cursorDirty : 1;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtQuick/private/qsgrenderer_p.h>
|
||||
#include <QtQuick/private/qsgplaintexture_p.h>
|
||||
#include <QtQuick/private/qquickpointerhandler_p.h>
|
||||
#include <QtQuick/private/qquickpointerhandler_p_p.h>
|
||||
#include <private/qsgrenderloop_p.h>
|
||||
#include <private/qsgrhisupport_p.h>
|
||||
#include <private/qquickrendercontrol_p.h>
|
||||
|
@ -1684,11 +1685,14 @@ void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *root
|
|||
if (!rootItem)
|
||||
rootItem = contentItem;
|
||||
auto cursorItemAndHandler = findCursorItemAndHandler(rootItem, scenePos);
|
||||
if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
|
||||
if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second ||
|
||||
(cursorItemAndHandler.second && QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty)) {
|
||||
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(q);
|
||||
QWindow *window = renderWindow ? renderWindow : q;
|
||||
cursorItem = cursorItemAndHandler.first;
|
||||
cursorHandler = cursorItemAndHandler.second;
|
||||
if (cursorHandler)
|
||||
QQuickPointerHandlerPrivate::get(cursorItemAndHandler.second)->cursorDirty = false;
|
||||
if (cursorItem) {
|
||||
const auto cursor = QQuickItemPrivate::get(cursorItem)->effectiveCursor(cursorHandler);
|
||||
qCDebug(lcHoverTrace) << "setting cursor" << cursor << "from" << cursorHandler << "or" << cursorItem;
|
||||
|
|
|
@ -1686,7 +1686,13 @@ void QQuickDeliveryAgentPrivate::flushFrameSynchronousEvents(QQuickWindow *win)
|
|||
if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
|
||||
!lastMousePosition.isNull() && QQuickWindowPrivate::get(win)->dirtyItemList) {
|
||||
qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root @" << lastMousePosition;
|
||||
deliverHoverEvent(lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0);
|
||||
if (deliverHoverEvent(lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0)) {
|
||||
#if QT_CONFIG(cursor)
|
||||
QQuickWindowPrivate::get(rootItem->window())->updateCursor(
|
||||
sceneTransform ? sceneTransform->map(lastMousePosition) : lastMousePosition, rootItem);
|
||||
#endif
|
||||
}
|
||||
|
||||
qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: brownRect
|
||||
objectName: "brownRect"
|
||||
|
||||
width: 400
|
||||
height: 400
|
||||
|
||||
HoverHandler {
|
||||
id: hh
|
||||
cursorShape: parent.colorIndex == 0 ?
|
||||
Qt.CrossCursor :
|
||||
Qt.OpenHandCursor
|
||||
}
|
||||
|
||||
property list<color> colors: ["beige", "brown"]
|
||||
property int colorIndex: 0
|
||||
|
||||
color: colors[colorIndex]
|
||||
|
||||
Timer {
|
||||
id: colorTimer
|
||||
interval: 200
|
||||
running: true
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
parent.colorIndex = (parent.colorIndex + 1) % parent.colors.length;
|
||||
parent.color = parent.colors[parent.colorIndex];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ private slots:
|
|||
void deviceCursor();
|
||||
void addHandlerFromCpp();
|
||||
void ensureHoverHandlerWorksWhenItemHasHoverDisabled();
|
||||
void changeCursor();
|
||||
|
||||
private:
|
||||
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
|
||||
|
@ -671,6 +672,34 @@ void tst_HoverHandler::ensureHoverHandlerWorksWhenItemHasHoverDisabled()
|
|||
QCOMPARE(spy.size(), 2);
|
||||
}
|
||||
|
||||
void tst_HoverHandler::changeCursor()
|
||||
{
|
||||
QScopedPointer<QQuickView> windowPtr;
|
||||
createView(windowPtr, "changingCursor.qml");
|
||||
QQuickView * window = windowPtr.data();
|
||||
window->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
|
||||
QQuickItem *item = window->findChild<QQuickItem *>("brownRect");
|
||||
QVERIFY(item);
|
||||
QQuickHoverHandler *hh = item->findChild<QQuickHoverHandler *>();
|
||||
QVERIFY(hh);
|
||||
|
||||
QPoint itemCenter(item->mapToScene(QPointF(item->width() / 2, item->height() / 2)).toPoint());
|
||||
QSignalSpy hoveredSpy(hh, SIGNAL(hoveredChanged()));
|
||||
|
||||
QTest::mouseMove(window, itemCenter);
|
||||
|
||||
QTRY_COMPARE(hoveredSpy.size(), 1);
|
||||
|
||||
#if QT_CONFIG(cursor)
|
||||
QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor);
|
||||
QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor);
|
||||
QTRY_COMPARE(window->cursor().shape(), Qt::CrossCursor);
|
||||
QTRY_COMPARE(window->cursor().shape(), Qt::OpenHandCursor);
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_HoverHandler)
|
||||
|
||||
#include "tst_qquickhoverhandler.moc"
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
width: 600
|
||||
height: 600
|
||||
|
||||
ListModel {
|
||||
id: cursorsModel
|
||||
ListElement { cursorShape: Qt.ArrowCursor; text: "Arrow" }
|
||||
ListElement { cursorShape: Qt.UpArrowCursor; text: "UpArrow" }
|
||||
ListElement { cursorShape: Qt.CrossCursor; text: "Cross" }
|
||||
ListElement { cursorShape: Qt.WaitCursor; text: "Wait" }
|
||||
ListElement { cursorShape: Qt.IBeamCursor; text: "IBeam" }
|
||||
ListElement { cursorShape: Qt.SizeVerCursor; text: "SizeVer" }
|
||||
ListElement { cursorShape: Qt.SizeHorCursor; text: "SizeHor" }
|
||||
ListElement { cursorShape: Qt.SizeBDiagCursor; text: "SizeBDiag" }
|
||||
ListElement { cursorShape: Qt.SizeFDiagCursor; text: "SizeFDiag" }
|
||||
ListElement { cursorShape: Qt.SizeAllCursor; text: "SizeAll" }
|
||||
ListElement { cursorShape: Qt.BlankCursor; text: "Blank" }
|
||||
ListElement { cursorShape: Qt.SplitVCursor; text: "SplitV" }
|
||||
ListElement { cursorShape: Qt.SplitHCursor; text: "SplitH" }
|
||||
ListElement { cursorShape: Qt.PointingHandCursor; text: "PointingHand" }
|
||||
ListElement { cursorShape: Qt.ForbiddenCursor; text: "Forbidden" }
|
||||
ListElement { cursorShape: Qt.WhatsThisCursor; text: "WhatsThis" }
|
||||
ListElement { cursorShape: Qt.BusyCursor; text: "Busy" }
|
||||
ListElement { cursorShape: Qt.OpenHandCursor; text: "OpenHand" }
|
||||
ListElement { cursorShape: Qt.ClosedHandCursor; text: "ClosedHand" }
|
||||
ListElement { cursorShape: Qt.DragCopyCursor; text: "DragCopy" }
|
||||
ListElement { cursorShape: Qt.DragMoveCursor; text: "DragMove" }
|
||||
ListElement { cursorShape: Qt.DragLinkCursor; text: "DragLink" }
|
||||
}
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: flow.height
|
||||
Flow {
|
||||
id: flow
|
||||
width: parent.width
|
||||
Repeater {
|
||||
model: cursorsModel
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "white"
|
||||
border.width: 5
|
||||
border.color: "black"
|
||||
|
||||
width: 200
|
||||
height: 200
|
||||
|
||||
Text {
|
||||
id: textItem
|
||||
anchors.fill: parent
|
||||
anchors.margins: parent.width * 0.1
|
||||
text: model.text
|
||||
fontSizeMode: Text.Fit
|
||||
minimumPixelSize: 10; font.pixelSize: height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: model.cursorShape
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,11 @@
|
|||
|
||||
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
|
||||
|
||||
static bool isPlatformWayland()
|
||||
{
|
||||
return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
class CircleMask : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -121,6 +126,7 @@ private slots:
|
|||
void changeAxis();
|
||||
#if QT_CONFIG(cursor)
|
||||
void cursorShape();
|
||||
void cursorUpdating();
|
||||
#endif
|
||||
void moveAndReleaseWithoutPress();
|
||||
void nestedStopAtBounds();
|
||||
|
@ -1901,6 +1907,38 @@ void tst_QQuickMouseArea::cursorShape()
|
|||
QCOMPARE(mouseArea->cursor().shape(), Qt::WaitCursor);
|
||||
QCOMPARE(spy.size(), 2);
|
||||
}
|
||||
|
||||
void tst_QQuickMouseArea::cursorUpdating()
|
||||
{
|
||||
QQuickView window;
|
||||
QVERIFY(QQuickTest::showView(window, testFileUrl("cursorUpdating.qml")));
|
||||
QQuickItem *root = window.rootObject();
|
||||
QVERIFY(root);
|
||||
QQuickFlickable *flickable = root->findChild<QQuickFlickable*>();
|
||||
QVERIFY(flickable);
|
||||
QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root);
|
||||
QVERIFY(rootPrivate->subtreeCursorEnabled);
|
||||
|
||||
QTest::mouseMove(&window, QPoint(40, 40));
|
||||
QCOMPARE(window.cursor().shape(), Qt::ArrowCursor);
|
||||
|
||||
QTest::mouseMove(&window, QPoint(240, 40));
|
||||
QCOMPARE(window.cursor().shape(), Qt::UpArrowCursor);
|
||||
|
||||
if (isPlatformWayland())
|
||||
QSKIP("Wayland: QCursor::setPos() doesn't work.");
|
||||
|
||||
// QTBUG-53987: with the cursor physically hovering, use wheel to
|
||||
// position a different item that requests a different cursor
|
||||
const QPoint p(240, 40);
|
||||
const QPoint pg = window.mapToGlobal(p);
|
||||
QCursor::setPos(pg);
|
||||
QWheelEvent wheelEvent(p, pg, QPoint(60, -400), QPoint(0, -600),
|
||||
Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false);
|
||||
QGuiApplication::sendEvent(&window, &wheelEvent);
|
||||
QTRY_VERIFY(flickable->contentY() > 300);
|
||||
QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QQuickMouseArea::moveAndReleaseWithoutPress()
|
||||
|
|
Loading…
Reference in New Issue