winrt: Fix input grabbing

Beside its usage in widgets, mouse grabs are required for QML menus to
work.

Task-number: QTBUG-57079
Change-Id: I306cb68624186da69725470e147bc7b979dac8e4
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Maurice Kalinowski 2016-11-24 15:09:25 +01:00 committed by Jani Heikkinen
parent 10143ea803
commit 181860e1af
4 changed files with 106 additions and 3 deletions

View File

@ -486,6 +486,9 @@ public:
QHash<ApplicationView2CallbackRemover, EventRegistrationToken> view2Tokens;
ComPtr<IApplicationView2> view2;
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
QAtomicPointer<QWinRTWindow> mouseGrabWindow;
QAtomicPointer<QWinRTWindow> keyboardGrabWindow;
QWindow *currentPressWindow = 0;
};
// To be called from the XAML thread
@ -877,6 +880,44 @@ void QWinRTScreen::lower(QWindow *window)
handleExpose();
}
bool QWinRTScreen::setMouseGrabWindow(QWinRTWindow *window, bool grab)
{
Q_D(QWinRTScreen);
qCDebug(lcQpaWindows) << __FUNCTION__ << window
<< "(" << window->window()->objectName() << "):" << grab;
if (!grab || window == nullptr)
d->mouseGrabWindow = nullptr;
else if (d->mouseGrabWindow != window)
d->mouseGrabWindow = window;
return grab;
}
QWinRTWindow *QWinRTScreen::mouseGrabWindow() const
{
Q_D(const QWinRTScreen);
return d->mouseGrabWindow;
}
bool QWinRTScreen::setKeyboardGrabWindow(QWinRTWindow *window, bool grab)
{
Q_D(QWinRTScreen);
qCDebug(lcQpaWindows) << __FUNCTION__ << window
<< "(" << window->window()->objectName() << "):" << grab;
if (!grab || window == nullptr)
d->keyboardGrabWindow = nullptr;
else if (d->keyboardGrabWindow != window)
d->keyboardGrabWindow = window;
return grab;
}
QWinRTWindow *QWinRTScreen::keyboardGrabWindow() const
{
Q_D(const QWinRTScreen);
return d->keyboardGrabWindow;
}
void QWinRTScreen::updateWindowTitle(const QString &title)
{
Q_D(QWinRTScreen);
@ -1022,7 +1063,11 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args)
pointerPoint->get_Position(&point);
QPoint pos(point.X * d->scaleFactor, point.Y * d->scaleFactor);
QWindowSystemInterface::handleEnterEvent(topWindow(), pos, pos);
QWindow *targetWindow = topWindow();
if (d->mouseGrabWindow)
targetWindow = d->mouseGrabWindow.load()->window();
QWindowSystemInterface::handleEnterEvent(targetWindow, pos, pos);
}
return S_OK;
}
@ -1041,7 +1086,11 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args)
d->touchPoints.remove(id);
QWindowSystemInterface::handleLeaveEvent(0);
QWindow *targetWindow = nullptr;
if (d->mouseGrabWindow)
targetWindow = d->mouseGrabWindow.load()->window();
QWindowSystemInterface::handleLeaveEvent(targetWindow);
return S_OK;
}
@ -1063,7 +1112,12 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
QPointF localPos = pos;
const QPoint posPoint = pos.toPoint();
QWindow *targetWindow = windowAt(posPoint);
QWindow *windowUnderPointer = windowAt(posPoint);
QWindow *targetWindow = windowUnderPointer;
if (d->mouseGrabWindow)
targetWindow = d->mouseGrabWindow.load()->window();
if (targetWindow) {
const QPointF globalPosDelta = pos - posPoint;
localPos = targetWindow->mapFromGlobal(posPoint) + globalPosDelta;
@ -1127,6 +1181,22 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
if (isPressed)
buttons |= Qt::XButton2;
// 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 (buttons != Qt::NoButton && d->currentPressWindow == nullptr && !d->mouseGrabWindow)
d->currentPressWindow = windowUnderPointer;
if (!isPressed && d->currentPressWindow && d->mouseGrabWindow) {
const QPointF globalPosDelta = pos - posPoint;
const QPointF localPressPos = d->currentPressWindow->mapFromGlobal(posPoint) + globalPosDelta;
QWindowSystemInterface::handleMouseEvent(d->currentPressWindow, localPressPos, pos, buttons, mods);
d->currentPressWindow = nullptr;
}
QWindowSystemInterface::handleMouseEvent(targetWindow, localPos, pos, buttons, mods);
break;

View File

@ -83,6 +83,7 @@ class QTouchDevice;
class QWinRTCursor;
class QWinRTInputContext;
class QWinRTScreenPrivate;
class QWinRTWindow;
class QWinRTScreen : public QPlatformScreen
{
public:
@ -110,6 +111,12 @@ public:
void raise(QWindow *window);
void lower(QWindow *window);
bool setMouseGrabWindow(QWinRTWindow *window, bool grab);
QWinRTWindow* mouseGrabWindow() const;
bool setKeyboardGrabWindow(QWinRTWindow *window, bool grab);
QWinRTWindow* keyboardGrabWindow() const;
void updateWindowTitle(const QString &title);
ABI::Windows::UI::Core::ICoreWindow *coreWindow() const;

View File

@ -191,6 +191,11 @@ QWinRTWindow::~QWinRTWindow()
});
RETURN_VOID_IF_FAILED("Failed to completely destroy window resources, likely because the application is shutting down");
if (d->screen->mouseGrabWindow() == this)
d->screen->setMouseGrabWindow(this, false);
if (d->screen->keyboardGrabWindow() == this)
d->screen->setKeyboardGrabWindow(this, false);
d->screen->removeWindow(window());
if (!d->surface)
@ -384,6 +389,24 @@ void QWinRTWindow::setWindowState(Qt::WindowState state)
d->state = state;
}
bool QWinRTWindow::setMouseGrabEnabled(bool grab)
{
Q_D(QWinRTWindow);
if (!isActive() && grab) {
qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
__FUNCTION__, window()->metaObject()->className(),
qPrintable(window()->objectName()));
return false;
}
return d->screen->setMouseGrabWindow(this, grab);
}
bool QWinRTWindow::setKeyboardGrabEnabled(bool grab)
{
Q_D(QWinRTWindow);
return d->screen->setKeyboardGrabWindow(this, grab);
}
EGLSurface QWinRTWindow::eglSurface() const
{
Q_D(const QWinRTWindow);

View File

@ -70,6 +70,9 @@ public:
qreal devicePixelRatio() const Q_DECL_OVERRIDE;
void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE;
bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE;
bool setKeyboardGrabEnabled(bool grab) Q_DECL_OVERRIDE;
EGLSurface eglSurface() const;
void createEglSurface(EGLDisplay display, EGLConfig config);