summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@qt.io>2018-07-12 13:01:14 +0200
committerOliver Wolff <oliver.wolff@qt.io>2018-08-06 05:24:35 +0000
commite05dc08ba02b4a9d893190230c8dfd2554ec4413 (patch)
tree44e4ecde31d11f5a8568304a45861a59e6b2deb0
parent01e57909d4bce31483d023cf11dd2298391b38f3 (diff)
winrt: Implement QPlatformCursor::setPos
Additionally to setting the cursor position we have to make sure that enter and leave events are triggered. As WinRT at the moment only supports maximized/fullscreen native top level widgets, an enter or leave event has to be triggered, every time the cursor enters or leaves the core window. Same as is done on Windows desktop an enter event is immediately followed by a move event even for emulated mouse events. Change-Id: I4b9a7b07f8e24b7887619f96979a064d933788aa Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
-rw-r--r--src/plugins/platforms/winrt/qwinrtcursor.cpp40
-rw-r--r--src/plugins/platforms/winrt/qwinrtcursor.h1
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.cpp59
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.h9
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_qwindow.cpp5
-rw-r--r--tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp3
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp4
7 files changed, 112 insertions, 9 deletions
diff --git a/src/plugins/platforms/winrt/qwinrtcursor.cpp b/src/plugins/platforms/winrt/qwinrtcursor.cpp
index 3c918df935..6236ec1f2b 100644
--- a/src/plugins/platforms/winrt/qwinrtcursor.cpp
+++ b/src/plugins/platforms/winrt/qwinrtcursor.cpp
@@ -56,6 +56,11 @@ using namespace ABI::Windows::Foundation;
QT_BEGIN_NAMESPACE
+static inline bool qIsPointInRect(const Point &p, const Rect &r)
+{
+ return (p.X >= r.X && p.Y >= r.Y && p.X < r.X + r.Width && p.Y < r.Y + r.Height);
+}
+
class QWinRTCursorPrivate
{
public:
@@ -179,5 +184,40 @@ QPoint QWinRTCursor::pos() const
: position;
}
+void QWinRTCursor::setPos(const QPoint &pos)
+{
+ QWinRTScreen *screen = static_cast<QWinRTScreen *>(QGuiApplication::primaryScreen()->handle());
+ Q_ASSERT(screen);
+ ComPtr<ICoreWindow> coreWindow = screen->coreWindow();
+ Q_ASSERT(coreWindow);
+ const QPointF scaledPos = pos / screen->scaleFactor();
+ QWinRTScreen::MousePositionTransition t;
+ HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, scaledPos, &t]() {
+ ComPtr<ICoreWindow2> coreWindow2;
+ HRESULT hr = coreWindow.As(&coreWindow2);
+ RETURN_HR_IF_FAILED("Failed to cast core window.");
+ Rect bounds;
+ hr = coreWindow->get_Bounds(&bounds);
+ RETURN_HR_IF_FAILED("Failed to obtain window bounds.");
+ Point mousePos;
+ hr = coreWindow->get_PointerPosition(&mousePos);
+ RETURN_HR_IF_FAILED("Failed to obtain mouse position.");
+ const Point p = {FLOAT(scaledPos.x() + bounds.X), FLOAT(scaledPos.y() + bounds.Y)};
+ const bool wasInWindow = qIsPointInRect(mousePos, bounds);
+ const bool willBeInWindow = qIsPointInRect(p, bounds);
+ if (wasInWindow && willBeInWindow)
+ t = QWinRTScreen::MousePositionTransition::StayedIn;
+ else if (wasInWindow && !willBeInWindow)
+ t = QWinRTScreen::MousePositionTransition::MovedOut;
+ else if (!wasInWindow && willBeInWindow)
+ t = QWinRTScreen::MousePositionTransition::MovedIn;
+ else
+ t = QWinRTScreen::MousePositionTransition::StayedOut;
+ return coreWindow2->put_PointerPosition(p);
+ });
+ RETURN_VOID_IF_FAILED("Failed to set cursor position");
+ screen->emulateMouseMove(scaledPos, t);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrtcursor.h b/src/plugins/platforms/winrt/qwinrtcursor.h
index 7f579f1531..253827f38f 100644
--- a/src/plugins/platforms/winrt/qwinrtcursor.h
+++ b/src/plugins/platforms/winrt/qwinrtcursor.h
@@ -54,6 +54,7 @@ public:
void changeCursor(QCursor * windowCursor, QWindow *window) override;
#endif
QPoint pos() const override;
+ void setPos(const QPoint &pos) override;
private:
QScopedPointer<QWinRTCursorPrivate> d_ptr;
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp
index 7d6659f5e2..0c03c2865b 100644
--- a/src/plugins/platforms/winrt/qwinrtscreen.cpp
+++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp
@@ -491,6 +491,7 @@ public:
QAtomicPointer<QWinRTWindow> keyboardGrabWindow;
QWindow *currentPressWindow = nullptr;
QWindow *currentTargetWindow = nullptr;
+ bool firstMouseMove = true;
};
// To be called from the XAML thread
@@ -850,6 +851,7 @@ void QWinRTScreen::addWindow(QWindow *window)
}
handleExpose();
+ d->firstMouseMove = true;
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
#if QT_CONFIG(draganddrop)
@@ -1091,6 +1093,7 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args)
d->currentTargetWindow = d->mouseGrabWindow.load()->window();
QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, pos, pos);
+ d->firstMouseMove = false;
}
return S_OK;
}
@@ -1315,6 +1318,62 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
return S_OK;
}
+void QWinRTScreen::emulateMouseMove(const QPointF &point, MousePositionTransition transition)
+{
+ Q_D(QWinRTScreen);
+ if (transition == MousePositionTransition::StayedOut)
+ return;
+ qt_winrt_lastPointerPoint = nullptr;
+ const QPointF pos(point.x() * d->scaleFactor, point.y() * d->scaleFactor);
+ QPointF localPos = pos;
+
+ const QPoint posPoint = pos.toPoint();
+ QWindow *windowUnderPointer = windowAt(QHighDpiScaling::mapPositionFromNative(posPoint, this));
+ d->currentTargetWindow = windowUnderPointer;
+
+ if (d->mouseGrabWindow)
+ d->currentTargetWindow = d->mouseGrabWindow.load()->window();
+
+ if (d->currentTargetWindow) {
+ const QPointF globalPosDelta = pos - posPoint;
+ localPos = d->currentTargetWindow->mapFromGlobal(posPoint) + globalPosDelta;
+ }
+
+ // In case of a mouse grab we have to store the target of a press event
+ // to be able to send one additional release event to this target when the mouse
+ // button is released. This is a similar approach to AutoMouseCapture in the
+ // windows qpa backend. Otherwise the release might not be propagated and the original
+ // press event receiver considers a button to still be pressed, as in Qt Quick Controls 1
+ // menus.
+ if (d->currentPressWindow && d->mouseGrabWindow) {
+ const QPointF globalPosDelta = pos - posPoint;
+ const QPointF localPressPos = d->currentPressWindow->mapFromGlobal(posPoint) + globalPosDelta;
+
+ QWindowSystemInterface::handleMouseEvent(d->currentPressWindow, localPressPos, pos,
+ Qt::NoButton, Qt::NoModifier);
+ d->currentPressWindow = nullptr;
+ }
+ // If the mouse button is released outside of a window, targetWindow is 0, but the event
+ // has to be delivered to the window, that initially received the mouse press. Do not reset
+ // d->currentTargetWindow though, as it is used (and reset) in onPointerExited.
+ if (d->currentPressWindow && !d->currentTargetWindow) {
+ d->currentTargetWindow = d->currentPressWindow;
+ d->currentPressWindow = nullptr;
+ }
+
+ if (transition == MousePositionTransition::MovedOut) {
+ QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow);
+ return;
+ }
+
+ if (transition == MousePositionTransition::MovedIn || d->firstMouseMove) {
+ QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, localPos, pos);
+ d->firstMouseMove = false;
+ }
+ QWindowSystemInterface::handleMouseEvent(d->currentTargetWindow, localPos, pos, Qt::NoButton,
+ Qt::NoModifier);
+}
+
HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args)
{
Q_D(QWinRTScreen);
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.h b/src/plugins/platforms/winrt/qwinrtscreen.h
index fd6499c2b9..6d0d3cdf52 100644
--- a/src/plugins/platforms/winrt/qwinrtscreen.h
+++ b/src/plugins/platforms/winrt/qwinrtscreen.h
@@ -128,6 +128,15 @@ public:
void setCursorRect(const QRectF &cursorRect);
void setKeyboardRect(const QRectF &keyboardRect);
+ enum class MousePositionTransition {
+ MovedOut,
+ MovedIn,
+ StayedIn,
+ StayedOut
+ };
+
+ void emulateMouseMove(const QPointF &point, MousePositionTransition transition);
+
private:
void handleExpose();
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index fd0a051390..7c24bbaadd 100644
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
@@ -2104,8 +2104,6 @@ void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
// Wait for the enter event. It must be delivered here, otherwise second
// compare can PASS because of this event even after "resetCounters()".
- if (isPlatformWinRT())
- QEXPECT_FAIL("", "WinRT does not support QCursor::setPos.", Abort);
QTRY_COMPARE(root.enterEventCount, 1);
QTRY_COMPARE(root.leaveEventCount, 0);
@@ -2126,6 +2124,9 @@ void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
root.resetCounters();
modal.close();
+ if (isPlatformWinRT())
+ QEXPECT_FAIL("", "WinRT does not trigger the enter event correctly"
+ "- QTBUG-68297.", Abort);
// Check for the enter event
QTRY_COMPARE(root.enterEventCount, 1);
}
diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp
index c8b13b0f90..45c86800d6 100644
--- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp
+++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp
@@ -1238,9 +1238,6 @@ void tst_QAbstractItemView::task200665_itemEntered()
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QCursor::setPos(view.geometry().center());
-#ifdef Q_OS_WINRT
- QEXPECT_FAIL("", "QCursor::setPos does not work on WinRT", Abort);
-#endif
QTRY_COMPARE(QCursor::pos(), view.geometry().center());
QSignalSpy spy(&view, SIGNAL(entered(QModelIndex)));
view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum());
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index cb0a225195..e30429b7e7 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -9119,8 +9119,6 @@ void tst_QWidget::syntheticEnterLeave()
QCOMPARE(child1->numLeaveEvents, 0);
// This event arrives asynchronously
- if (m_platform == QStringLiteral("winrt"))
- QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
QTRY_COMPARE(window.numEnterEvents, 1);
QCOMPARE(child2->numEnterEvents, 1);
QCOMPARE(grandChild->numEnterEvents, 1);
@@ -9205,8 +9203,6 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave()
QCursor::setPos(child.mapToGlobal(QPoint(100, 100)));
// Make sure the cursor has entered the child.
- if (m_platform == QStringLiteral("winrt"))
- QEXPECT_FAIL("", "WinRT: This fails. QTBUG-68297.", Abort);
QTRY_VERIFY(child.numEnterEvents > 0);
child.hide();