winrt: Make sure that cursor is visible when virtual keyboard is shown

We have to check whether the cursor is covered by the virtual keyboard
when it is shown. If that is the case and the keyboard is snapped to the
bottom of the screen the whole content is moved up to ensure the
cursors's visibility.

WinRT's input context had to be moved from the XAML to the GUI thread
as the signal/slot connection does not work otherwise. Signals from
QInputMethod were emitted but not handled in QWinRTInputContext as it
ran on another thread which did not spin an event loop.

Task-number: QTBUG-50291
Change-Id: Id89e970dc194c25ad07ceab14d9fea51bd7388b2
Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
This commit is contained in:
Oliver Wolff 2016-09-01 14:06:10 +02:00 committed by Maurice Kalinowski
parent a5915f260f
commit f104e43a72
5 changed files with 69 additions and 6 deletions

View File

@ -39,6 +39,7 @@
#include "qwinrtinputcontext.h"
#include "qwinrtscreen.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
#include <private/qeventdispatcher_winrt_p.h>
@ -95,17 +96,22 @@ QWinRTInputContext::QWinRTInputContext(QWinRTScreen *screen)
ComPtr<IInputPane> inputPane;
statics->GetForCurrentView(&inputPane);
if (inputPane) {
EventRegistrationToken showToken, hideToken;
inputPane->add_Showing(Callback<InputPaneVisibilityHandler>(
this, &QWinRTInputContext::onShowing).Get(), &showToken);
inputPane->add_Hiding(Callback<InputPaneVisibilityHandler>(
this, &QWinRTInputContext::onHiding).Get(), &hideToken);
QEventDispatcherWinRT::runOnXamlThread([this, inputPane]() {
EventRegistrationToken showToken, hideToken;
inputPane->add_Showing(Callback<InputPaneVisibilityHandler>(
this, &QWinRTInputContext::onShowing).Get(), &showToken);
inputPane->add_Hiding(Callback<InputPaneVisibilityHandler>(
this, &QWinRTInputContext::onHiding).Get(), &hideToken);
return S_OK;
});
m_keyboardRect = getInputPaneRect(inputPane, m_screen->scaleFactor());
m_isInputPanelVisible = !m_keyboardRect.isEmpty();
} else {
qWarning("failed to retrieve InputPane.");
}
connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
this, &QWinRTInputContext::updateScreenCursorRect);
}
QRectF QWinRTInputContext::keyboardRect() const
@ -118,6 +124,11 @@ bool QWinRTInputContext::isInputPanelVisible() const
return m_isInputPanelVisible;
}
void QWinRTInputContext::updateScreenCursorRect()
{
m_screen->setCursorRect(QGuiApplication::inputMethod()->cursorRectangle());
}
HRESULT QWinRTInputContext::onShowing(IInputPane *pane, IInputPaneVisibilityEventArgs *)
{
qCDebug(lcQpaInputMethods) << __FUNCTION__ << pane;
@ -140,6 +151,7 @@ HRESULT QWinRTInputContext::handleVisibilityChange(IInputPane *pane)
const QRectF keyboardRect = getInputPaneRect(pane, m_screen->scaleFactor());
if (m_keyboardRect != keyboardRect) {
m_keyboardRect = keyboardRect;
m_screen->setKeyboardRect(m_keyboardRect);
emitKeyboardRectChanged();
}
return S_OK;

View File

@ -79,6 +79,9 @@ public:
void hideInputPanel();
#endif
private slots:
void updateScreenCursorRect();
private:
HRESULT onShowing(ABI::Windows::UI::ViewManagement::IInputPane *,
ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs *);
@ -89,6 +92,7 @@ private:
QWinRTScreen *m_screen;
QRectF m_keyboardRect;
QRectF m_cursorRect;
bool m_isInputPanelVisible;
};

View File

@ -197,9 +197,9 @@ QWinRTIntegration::QWinRTIntegration() : d_ptr(new QWinRTIntegrationPrivate)
QEventDispatcherWinRT::runOnXamlThread([d]() {
d->mainScreen = new QWinRTScreen;
d->inputContext.reset(new QWinRTInputContext(d->mainScreen));
return S_OK;
});
d->inputContext.reset(new QWinRTInputContext(d->mainScreen));
screenAdded(d->mainScreen);
d->platformServices = new QWinRTServices;

View File

@ -753,6 +753,49 @@ void QWinRTScreen::initialize()
onVisibilityChanged(nullptr, nullptr);
}
void QWinRTScreen::setCursorRect(const QRectF &cursorRect)
{
mCursorRect = cursorRect;
}
void QWinRTScreen::setKeyboardRect(const QRectF &keyboardRect)
{
Q_D(QWinRTScreen);
QRectF visibleRectF;
HRESULT hr;
Rect windowSize;
hr = d->coreWindow->get_Bounds(&windowSize);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get window bounds");
return;
}
d->logicalRect = QRectF(windowSize.X, windowSize.Y, windowSize.Width, windowSize.Height);
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
Rect visibleRect;
hr = d->view2->get_VisibleBounds(&visibleRect);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get window visible bounds");
return;
}
visibleRectF = QRectF(visibleRect.X, visibleRect.Y, visibleRect.Width, visibleRect.Height);
#else
visibleRectF = d->logicalRect;
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
// if keyboard is snapped to the bottom of the screen and would cover the cursor the content is
// moved up to make it visible
if (keyboardRect.intersects(mCursorRect)
&& qFuzzyCompare(geometry().height(), keyboardRect.y() + keyboardRect.height())) {
visibleRectF.moveTop(visibleRectF.top() - keyboardRect.height() / d->scaleFactor);
}
d->visibleRect = visibleRectF;
qCDebug(lcQpaWindows) << __FUNCTION__ << d->visibleRect;
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry());
QPlatformScreen::resizeMaximizedWindows();
handleExpose();
}
QWindow *QWinRTScreen::topWindow() const
{
Q_D(const QWinRTScreen);

View File

@ -116,6 +116,9 @@ public:
void initialize();
void setCursorRect(const QRectF &cursorRect);
void setKeyboardRect(const QRectF &keyboardRect);
private:
void handleExpose();
@ -140,6 +143,7 @@ private:
#endif
QScopedPointer<QWinRTScreenPrivate> d_ptr;
QRectF mCursorRect;
Q_DECLARE_PRIVATE(QWinRTScreen)
};