Skip to content

Commit

Permalink
winrt: Fix input grabbing
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
mauricek authored and jaheikk committed Dec 8, 2016
1 parent 10143ea commit 181860e
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 3 deletions.
76 changes: 73 additions & 3 deletions src/plugins/platforms/winrt/qwinrtscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ class QWinRTScreenPrivate
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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/platforms/winrt/qwinrtscreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class QTouchDevice;
class QWinRTCursor;
class QWinRTInputContext;
class QWinRTScreenPrivate;
class QWinRTWindow;
class QWinRTScreen : public QPlatformScreen
{
public:
Expand Down Expand Up @@ -110,6 +111,12 @@ class QWinRTScreen : public QPlatformScreen
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;
Expand Down
23 changes: 23 additions & 0 deletions src/plugins/platforms/winrt/qwinrtwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/platforms/winrt/qwinrtwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class QWinRTWindow : public QPlatformWindow
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);

Expand Down

0 comments on commit 181860e

Please sign in to comment.