Enhance editors notifying input panel for input method changes
authorPekka Vuorela <pekka.ta.vuorela@nokia.com>
Fri, 20 Jan 2012 12:07:20 +0000 (14:07 +0200)
committerQt by Nokia <qt-info@nokia.com>
Fri, 27 Jan 2012 14:38:09 +0000 (15:38 +0100)
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
src/quick/items/qquickitem.h
src/quick/items/qquicktextedit.cpp
src/quick/items/qquicktextinput.cpp
tests/auto/qtquick2/qquicktextedit/tst_qquicktextedit.cpp
tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp

index 4908463..844d1ed 100644 (file)
@@ -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
index 0dd896a..684910a 100644 (file)
@@ -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);
index 37c76c0..1260039 100644 (file)
@@ -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;
index 08892e1..9d36ded 100644 (file)
@@ -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)
index 1d63145..861f7a9 100644 (file)
@@ -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()
index cae38bc..8fc768f 100644 (file)
@@ -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()