From ed576da6712d881b87a3fffcbddbae925157d265 Mon Sep 17 00:00:00 2001
From: Pekka Vuorela <pekka.ta.vuorela@nokia.com>
Date: Fri, 20 Jan 2012 14:07:20 +0200
Subject: [PATCH] Enhance editors notifying input panel for input method
 changes

Introduced protected QQuickItem::updateInputMethod() and removed
similar, but badly named updateMicroFocus(). Added some missing
notifications from the editors and avoided unnecessary updates when not
having focus.

Change-Id: Id5c00e87dc26fd35c3f919006817511d4ed6418d
Reviewed-by: Andrew den Exter <andrew.den-exter@nokia.com>
Reviewed-by: Joona Petrell <joona.t.petrell@nokia.com>
---
 src/quick/items/qquickitem.cpp                | 16 +--
 src/quick/items/qquickitem.h                  |  3 +-
 src/quick/items/qquicktextedit.cpp            |  4 +-
 src/quick/items/qquicktextinput.cpp           | 23 ++---
 .../qquicktextedit/tst_qquicktextedit.cpp     | 99 ++++++++++++++++++-
 .../qquicktextinput/tst_qquicktextinput.cpp   | 30 +++++-
 6 files changed, 151 insertions(+), 24 deletions(-)

diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 49084639d5..844d1ede6b 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -3081,13 +3081,6 @@ void QQuickItem::setInputMethodHints(Qt::InputMethodHints hints)
         qApp->inputPanel()->update(Qt::ImHints);
 }
 
-void QQuickItem::updateMicroFocus()
-{
-    QInputPanel *p = qApp->inputPanel();
-    if (p->inputItem() == this)
-        qApp->inputPanel()->update(Qt::ImQueryInput);
-}
-
 QVariant QQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
 {
     Q_D(const QQuickItem);
@@ -3608,6 +3601,15 @@ void QQuickItem::itemChange(ItemChange change, const ItemChangeData &value)
     Q_UNUSED(value);
 }
 
+/*!
+    Notify input method on updated query values if needed. \a indicates changed attributes.
+*/
+void QQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
+{
+    if (hasActiveFocus())
+        qApp->inputPanel()->update(queries);
+}
+
 /*! \internal */
 // XXX todo - do we want/need this anymore?
 // Note that it's now used for varying clip rect
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 0dd896af3d..684910a112 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -324,7 +324,6 @@ public:
 
 public Q_SLOTS:
     void update();
-    void updateMicroFocus();
 
 Q_SIGNALS:
     void childrenRectChanged(const QRectF &);
@@ -359,6 +358,8 @@ protected:
     bool isComponentComplete() const;
     virtual void itemChange(ItemChange, const ItemChangeData &);
 
+    void updateInputMethod(Qt::InputMethodQueries queries = Qt::ImQueryInput);
+
     void setImplicitWidth(qreal);
     bool widthValid() const; // ### better name?
     void setImplicitHeight(qreal);
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 37c76c09a3..126003906e 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -373,6 +373,7 @@ void QQuickTextEdit::setFont(const QFont &font)
         }
         updateSize();
         updateDocument();
+        updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont);
     }
     emit fontChanged(d->sourceFont);
 }
@@ -1248,6 +1249,7 @@ void QQuickTextEdit::setReadOnly(bool r)
     if (!r)
         d->control->moveCursor(QTextCursor::End);
 
+    updateInputMethod(Qt::ImEnabled);
     q_canPasteChanged();
     emit readOnlyChanged(r);
 }
@@ -1853,7 +1855,7 @@ void QQuickTextEdit::moveCursorDelegate()
 {
     Q_D(QQuickTextEdit);
     d->determineHorizontalAlignment();
-    updateMicroFocus();
+    updateInputMethod();
     emit cursorRectangleChanged();
     if (!d->cursor)
         return;
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 08892e1922..9d36ded9cc 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -306,7 +306,7 @@ void QQuickTextInput::setFont(const QFont &font)
     if (oldFont != d->font) {
         d->updateLayout();
         updateCursorRectangle();
-        qApp->inputPanel()->update(Qt::ImCursorRectangle | Qt::ImFont);
+        updateInputMethod(Qt::ImCursorRectangle | Qt::ImFont);
     }
     emit fontChanged(d->sourceFont);
 }
@@ -575,6 +575,7 @@ void QQuickTextInput::setReadOnly(bool ro)
     d->m_readOnly = ro;
     if (!ro)
         d->setCursorPosition(d->end());
+    updateInputMethod(Qt::ImEnabled);
     q_canPasteChanged();
     d->emitUndoRedoChanged();
     emit readOnlyChanged(ro);
@@ -2508,6 +2509,7 @@ void QQuickTextInput::updateCursorRectangle()
         d->cursorItem->setPos(r.topLeft());
         d->cursorItem->setHeight(r.height());
     }
+    updateInputMethod(Qt::ImCursorRectangle);
 }
 
 void QQuickTextInput::selectionChanged()
@@ -2873,8 +2875,8 @@ void QQuickTextInputPrivate::setSelection(int start, int length)
     }
     emit q->selectionChanged();
     emitCursorPositionChanged();
-    qApp->inputPanel()->update(Qt::ImCursorRectangle | Qt::ImAnchorPosition
-                               | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+    q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+                        | Qt::ImCursorPosition | Qt::ImCurrentSelection);
 }
 
 /*!
@@ -2964,7 +2966,7 @@ void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
         emit q->selectionChanged();
     }
     emitCursorPositionChanged();
-    q->updateMicroFocus();
+    q->updateInputMethod();
 }
 
 /*!
@@ -3065,7 +3067,6 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
         emitCursorPositionChanged();
     } else if (m_preeditCursor != oldPreeditCursor) {
         q->updateCursorRectangle();
-        qApp->inputPanel()->update(Qt::ImCursorRectangle);
     }
 
     bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
@@ -3080,8 +3081,8 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
 
     if (selectionChange) {
         emit q->selectionChanged();
-        qApp->inputPanel()->update(Qt::ImCursorRectangle | Qt::ImAnchorPosition
-                                   | Qt::ImCursorPosition | Qt::ImCurrentSelection);
+        q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition
+                            | Qt::ImCursorPosition | Qt::ImCurrentSelection);
     }
 }
 
@@ -3123,7 +3124,7 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
     Q_Q(QQuickTextInput);
 
     Q_UNUSED(update)
-    bool notifyInputPanel = m_textDirty || m_selDirty;
+    bool inputMethodAttributesChanged = m_textDirty || m_selDirty;
     bool alignmentChanged = false;
 
     if (m_textDirty) {
@@ -3194,9 +3195,9 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
         emit q->selectionChanged();
     }
 
-    notifyInputPanel |= (m_cursor == m_lastCursorPos);
-    if (notifyInputPanel)
-        q->updateMicroFocus();
+    inputMethodAttributesChanged |= (m_cursor == m_lastCursorPos);
+    if (inputMethodAttributesChanged)
+        q->updateInputMethod();
     emitUndoRedoChanged();
 
     if (!emitCursorPositionChanged() && alignmentChanged)
diff --git a/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp
index 1d63145b2f..861f7a9efc 100644
--- a/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp
@@ -144,6 +144,7 @@ private slots:
     void canPaste();
     void canPasteEmpty();
     void textInput();
+    void inputPanelUpdate();
     void openInputPanel();
     void geometrySignals();
     void pastingRichText_QTBUG_14003();
@@ -226,7 +227,7 @@ void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
 
 void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &sequence)
 {
-    for (uint i = 0; i < sequence.count(); ++i) {
+    for (int i = 0; i < sequence.count(); ++i) {
         const int key = sequence[i];
         const int modifiers = key & Qt::KeyboardModifierMask;
 
@@ -236,7 +237,7 @@ void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &seque
 
 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
 {
-    for (uint i = 0; i < sequence.count(); ++i)
+    for (int i = 0; i < sequence.count(); ++i)
         keys << Key(sequence[i], QChar());
     return keys;
 }
@@ -2131,6 +2132,100 @@ void tst_qquicktextedit::textInput()
     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event2);
     QCOMPARE(spy.count(), 1);
     QCOMPARE(edit->text(), QString("string"));
+
+    QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
+    QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &queryEvent);
+    QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
+
+    edit->setReadOnly(true);
+    QGuiApplication::sendEvent(edit, &queryEvent);
+    QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
+}
+
+void tst_qquicktextedit::inputPanelUpdate()
+{
+    PlatformInputContext platformInputContext;
+    QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
+    inputPanelPrivate->testContext = &platformInputContext;
+
+    QQuickView view(testFileUrl("inputMethodEvent.qml"));
+    view.show();
+    view.requestActivateWindow();
+    QTest::qWaitForWindowShown(&view);
+    QTRY_COMPARE(&view, qGuiApp->focusWindow());
+    QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
+    QVERIFY(edit);
+    QVERIFY(edit->hasActiveFocus() == true);
+
+    // text change even without cursor position change needs to trigger update
+    edit->setText("test");
+    platformInputContext.clear();
+    edit->setText("xxxx");
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // input method event replacing text
+    platformInputContext.clear();
+    {
+        QInputMethodEvent inputMethodEvent;
+        inputMethodEvent.setCommitString("y", -1, 1);
+        QGuiApplication::sendEvent(edit, &inputMethodEvent);
+    }
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // input method changing selection
+    platformInputContext.clear();
+    {
+        QList<QInputMethodEvent::Attribute> attributes;
+        attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
+        QInputMethodEvent inputMethodEvent("", attributes);
+        QGuiApplication::sendEvent(edit, &inputMethodEvent);
+    }
+    QVERIFY(edit->selectionStart() != edit->selectionEnd());
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // font changes
+    platformInputContext.clear();
+    QFont font = edit->font();
+    font.setBold(!font.bold());
+    edit->setFont(font);
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // normal input
+    platformInputContext.clear();
+    {
+        QInputMethodEvent inputMethodEvent;
+        inputMethodEvent.setCommitString("y");
+        QGuiApplication::sendEvent(edit, &inputMethodEvent);
+    }
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // changing cursor position
+    platformInputContext.clear();
+    edit->setCursorPosition(0);
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // continuing with selection
+    platformInputContext.clear();
+    edit->moveCursorSelection(1);
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // read only disabled input method
+    platformInputContext.clear();
+    edit->setReadOnly(true);
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+    edit->setReadOnly(false);
+
+    // no updates while no focus
+    edit->setFocus(false);
+    platformInputContext.clear();
+    edit->setText("Foo");
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    edit->setCursorPosition(1);
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    edit->selectAll();
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    edit->setReadOnly(true);
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
 }
 
 void tst_qquicktextedit::openInputPanel()
diff --git a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
index cae38bcd73..8fc768f2ab 100644
--- a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
@@ -222,7 +222,7 @@ void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
 
 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
 {
-    for (uint i = 0; i < sequence.count(); ++i) {
+    for (int i = 0; i < sequence.count(); ++i) {
         const int key = sequence[i];
         const int modifiers = key & Qt::KeyboardModifierMask;
 
@@ -232,7 +232,7 @@ void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequ
 
 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
 {
-    for (uint i = 0; i < sequence.count(); ++i)
+    for (int i = 0; i < sequence.count(); ++i)
         keys << Key(sequence[i], QChar());
     return keys;
 }
@@ -1951,6 +1951,14 @@ void tst_qquicktextinput::inputMethods()
     event.setCommitString("replacement", -input->text().length(), input->text().length());
     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
     QCOMPARE(input->selectionStart(), input->selectionEnd());
+
+    QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
+    QGuiApplication::sendEvent(input, &enabledQueryEvent);
+    QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
+
+    input->setReadOnly(true);
+    QGuiApplication::sendEvent(input, &enabledQueryEvent);
+    QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
 }
 
 /*
@@ -3081,6 +3089,24 @@ void tst_qquicktextinput::inputPanelUpdate()
     platformInputContext.clear();
     input->setCursorPosition(0);
     QVERIFY(platformInputContext.m_updateCallCount > 0);
+
+    // read only disabled input method
+    platformInputContext.clear();
+    input->setReadOnly(true);
+    QVERIFY(platformInputContext.m_updateCallCount > 0);
+    input->setReadOnly(false);
+
+    // no updates while no focus
+    input->setFocus(false);
+    platformInputContext.clear();
+    input->setText("Foo");
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    input->setCursorPosition(1);
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    input->selectAll();
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
+    input->setReadOnly(true);
+    QCOMPARE(platformInputContext.m_updateCallCount, 0);
 }
 
 void tst_qquicktextinput::cursorRectangleSize()
-- 
GitLab