Implement focus change for Windows Input context.

Keep track of the focus object in the input context ensuring
that events on canceling are sent to the right object.

Task-number: QTBUG-40402
Change-Id: I79820db94d97e21b47abc8fe2bae6fa012d31236
Reviewed-by: Liang Qi <liang.qi@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Friedemann Kleint 2014-07-30 15:13:41 +02:00
parent 219c2eb4e2
commit 501fe9f939
2 changed files with 33 additions and 19 deletions

View File

@ -83,6 +83,10 @@ static inline QByteArray debugComposition(int lParam)
// Cancel current IME composition. // Cancel current IME composition.
static inline void imeNotifyCancelComposition(HWND hwnd) static inline void imeNotifyCancelComposition(HWND hwnd)
{ {
if (!hwnd) {
qWarning() << __FUNCTION__ << "called with" << hwnd;
return;
}
const HIMC himc = ImmGetContext(hwnd); const HIMC himc = ImmGetContext(hwnd);
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ImmReleaseContext(hwnd, himc); ImmReleaseContext(hwnd, himc);
@ -175,21 +179,27 @@ void QWindowsInputContext::reset()
QPlatformInputContext::reset(); QPlatformInputContext::reset();
if (!m_compositionContext.hwnd) if (!m_compositionContext.hwnd)
return; return;
QObject *fo = qApp->focusObject(); qCDebug(lcQpaInputMethods) << __FUNCTION__;
qCDebug(lcQpaInputMethods) << __FUNCTION__<< fo; if (m_compositionContext.isComposing && m_compositionContext.focusObject.isNull()) {
if (!fo)
return;
if (m_compositionContext.isComposing) {
QInputMethodEvent event; QInputMethodEvent event;
if (!m_compositionContext.composition.isEmpty()) if (!m_compositionContext.composition.isEmpty())
event.setCommitString(m_compositionContext.composition); event.setCommitString(m_compositionContext.composition);
QCoreApplication::sendEvent(fo, &event); QCoreApplication::sendEvent(m_compositionContext.focusObject, &event);
endContextComposition(); endContextComposition();
} }
imeNotifyCancelComposition(m_compositionContext.hwnd); imeNotifyCancelComposition(m_compositionContext.hwnd);
doneContext(); doneContext();
} }
void QWindowsInputContext::setFocusObject(QObject *)
{
// ### fixme: On Windows 8.1, it has been observed that the Input context
// remains active when this happens resulting in a lock-up. Consecutive
// key events still have VK_PROCESSKEY set and are thus ignored.
if (m_compositionContext.isComposing)
imeNotifyCancelComposition(m_compositionContext.hwnd);
}
/*! /*!
\brief Moves the candidate window along with microfocus of the focus object. \brief Moves the candidate window along with microfocus of the focus object.
*/ */
@ -317,7 +327,7 @@ static inline QTextFormat standardFormat(StandardFormat format)
bool QWindowsInputContext::startComposition(HWND hwnd) bool QWindowsInputContext::startComposition(HWND hwnd)
{ {
const QObject *fo = qApp->focusObject(); QObject *fo = QGuiApplication::focusObject();
if (!fo) if (!fo)
return false; return false;
// This should always match the object. // This should always match the object.
@ -327,7 +337,7 @@ bool QWindowsInputContext::startComposition(HWND hwnd)
qCDebug(lcQpaInputMethods) << __FUNCTION__ << fo << window; qCDebug(lcQpaInputMethods) << __FUNCTION__ << fo << window;
if (!fo || QWindowsWindow::handleOf(window) != hwnd) if (!fo || QWindowsWindow::handleOf(window) != hwnd)
return false; return false;
initContext(hwnd); initContext(hwnd, fo);
startContextComposition(); startContextComposition();
return true; return true;
} }
@ -341,6 +351,7 @@ void QWindowsInputContext::startContextComposition()
m_compositionContext.isComposing = true; m_compositionContext.isComposing = true;
m_compositionContext.composition.clear(); m_compositionContext.composition.clear();
m_compositionContext.position = 0; m_compositionContext.position = 0;
cursorRectChanged(); // position cursor initially.
update(Qt::ImQueryAll); update(Qt::ImQueryAll);
} }
@ -384,11 +395,10 @@ static inline QList<QInputMethodEvent::Attribute>
bool QWindowsInputContext::composition(HWND hwnd, LPARAM lParamIn) bool QWindowsInputContext::composition(HWND hwnd, LPARAM lParamIn)
{ {
QObject *fo = qApp->focusObject();
const int lParam = int(lParamIn); const int lParam = int(lParamIn);
qCDebug(lcQpaInputMethods) << '>' << __FUNCTION__ << fo << debugComposition(lParam) qCDebug(lcQpaInputMethods) << '>' << __FUNCTION__ << m_compositionContext.focusObject
<< " composing=" << m_compositionContext.isComposing; << debugComposition(lParam) << " composing=" << m_compositionContext.isComposing;
if (!fo || m_compositionContext.hwnd != hwnd || !lParam) if (m_compositionContext.focusObject.isNull() || m_compositionContext.hwnd != hwnd || !lParam)
return false; return false;
const HIMC himc = ImmGetContext(m_compositionContext.hwnd); const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
if (!himc) if (!himc)
@ -425,10 +435,10 @@ bool QWindowsInputContext::composition(HWND hwnd, LPARAM lParamIn)
event->setCommitString(getCompositionString(himc, GCS_RESULTSTR)); event->setCommitString(getCompositionString(himc, GCS_RESULTSTR));
endContextComposition(); endContextComposition();
} }
const bool result = QCoreApplication::sendEvent(fo, event.data()); const bool result = QCoreApplication::sendEvent(m_compositionContext.focusObject, event.data());
qCDebug(lcQpaInputMethods) << '<' << __FUNCTION__ << "sending markup=" qCDebug(lcQpaInputMethods) << '<' << __FUNCTION__ << "sending markup="
<< event->attributes().size() << " commit=" << event->commitString() << event->attributes().size() << " commit=" << event->commitString()
<< " to " << fo << " returns " << result; << " to " << m_compositionContext.focusObject << " returns " << result;
update(Qt::ImQueryAll); update(Qt::ImQueryAll);
ImmReleaseContext(m_compositionContext.hwnd, himc); ImmReleaseContext(m_compositionContext.hwnd, himc);
return result; return result;
@ -442,8 +452,7 @@ bool QWindowsInputContext::endComposition(HWND hwnd)
// against that. // against that.
if (m_endCompositionRecursionGuard || m_compositionContext.hwnd != hwnd) if (m_endCompositionRecursionGuard || m_compositionContext.hwnd != hwnd)
return false; return false;
QObject *fo = qApp->focusObject(); if (m_compositionContext.focusObject.isNull())
if (!fo)
return false; return false;
m_endCompositionRecursionGuard = true; m_endCompositionRecursionGuard = true;
@ -451,7 +460,7 @@ bool QWindowsInputContext::endComposition(HWND hwnd)
imeNotifyCancelComposition(m_compositionContext.hwnd); imeNotifyCancelComposition(m_compositionContext.hwnd);
if (m_compositionContext.isComposing) { if (m_compositionContext.isComposing) {
QInputMethodEvent event; QInputMethodEvent event;
QCoreApplication::sendEvent(fo, &event); QCoreApplication::sendEvent(m_compositionContext.focusObject, &event);
} }
doneContext(); doneContext();
@ -459,11 +468,12 @@ bool QWindowsInputContext::endComposition(HWND hwnd)
return true; return true;
} }
void QWindowsInputContext::initContext(HWND hwnd) void QWindowsInputContext::initContext(HWND hwnd, QObject *focusObject)
{ {
if (m_compositionContext.hwnd) if (m_compositionContext.hwnd)
doneContext(); doneContext();
m_compositionContext.hwnd = hwnd; m_compositionContext.hwnd = hwnd;
m_compositionContext.focusObject = focusObject;
// Create a hidden caret which is kept at the microfocus // Create a hidden caret which is kept at the microfocus
// position in update(). This is important for some // position in update(). This is important for some
// Chinese input methods. // Chinese input methods.
@ -484,6 +494,7 @@ void QWindowsInputContext::doneContext()
m_compositionContext.composition.clear(); m_compositionContext.composition.clear();
m_compositionContext.position = 0; m_compositionContext.position = 0;
m_compositionContext.isComposing = m_compositionContext.haveCaret = false; m_compositionContext.isComposing = m_compositionContext.haveCaret = false;
m_compositionContext.focusObject = 0;
} }
bool QWindowsInputContext::handleIME_Request(WPARAM wParam, bool QWindowsInputContext::handleIME_Request(WPARAM wParam,

View File

@ -44,6 +44,7 @@
#include "qtwindows_additional.h" #include "qtwindows_additional.h"
#include <QtCore/QPointer>
#include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontext.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -63,6 +64,7 @@ class QWindowsInputContext : public QPlatformInputContext
QString composition; QString composition;
int position; int position;
bool isComposing; bool isComposing;
QPointer<QObject> focusObject;
}; };
public: public:
explicit QWindowsInputContext(); explicit QWindowsInputContext();
@ -71,6 +73,7 @@ public:
void reset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE;
void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE; void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE;
void invokeAction(QInputMethod::Action, int cursorPosition) Q_DECL_OVERRIDE; void invokeAction(QInputMethod::Action, int cursorPosition) Q_DECL_OVERRIDE;
void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
static QWindowsInputContext *instance(); static QWindowsInputContext *instance();
@ -86,7 +89,7 @@ private slots:
void cursorRectChanged(); void cursorRectChanged();
private: private:
void initContext(HWND hwnd); void initContext(HWND hwnd, QObject *focusObject);
void doneContext(); void doneContext();
void startContextComposition(); void startContextComposition();
void endContextComposition(); void endContextComposition();