Support tentative commit string with input method.
authorPekka Vuorela <pekka.ta.vuorela@nokia.com>
Wed, 12 Oct 2011 08:08:00 +0000 (11:08 +0300)
committerQt by Nokia <qt-info@nokia.com>
Sat, 5 Nov 2011 22:47:02 +0000 (23:47 +0100)
Tentative commit string allows input method to notify editor
what is expected to be committed in the place of preedit.
This commit adds such support in QLineEdit.

Change-Id: If855619bc6843652db0d6254f7e7063bb8ad0936
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
src/gui/kernel/qevent.cpp
src/gui/kernel/qevent.h
src/widgets/widgets/qlineedit.cpp
src/widgets/widgets/qwidgetlinecontrol.cpp
src/widgets/widgets/qwidgetlinecontrol_p.h
tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp

index ba81e55..2c0858a 100644 (file)
@@ -1644,6 +1644,17 @@ void QInputMethodEvent::setCommitString(const QString &commitString, int replace
 }
 
 /*!
+    Sets the tentative commit string to \a tentativeCommitString.
+
+    The tentative commit string is what the preedit string is expected to be committed as.
+    The string can be used within the editor to trigger code that reacts on text changes such as validators.
+*/
+void QInputMethodEvent::setTentativeCommitString(const QString &tentativeCommitString)
+{
+    tentativeCommit = tentativeCommitString;
+}
+
+/*!
     \fn const QList<Attribute> &QInputMethodEvent::attributes() const
 
     Returns the list of attributes passed to the QInputMethodEvent
@@ -1692,6 +1703,14 @@ void QInputMethodEvent::setCommitString(const QString &commitString, int replace
     \sa replacementStart(), setCommitString()
 */
 
+/*!
+    \fn const QString &tentativeCommitString() const
+
+    Returns the text as which preedit string is expected to be committed as.
+    The string can be used within the editor to trigger code that reacts on text changes such as validators.
+
+    \sa setTentativeCommitString()
+*/
 
 /*! \class QInputMethodQueryEvent
 
index de88883..aad31a9 100644 (file)
@@ -428,6 +428,7 @@ public:
     QInputMethodEvent();
     QInputMethodEvent(const QString &preeditText, const QList<Attribute> &attributes);
     void setCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0);
+    void setTentativeCommitString(const QString &tentativeCommitString);
 
     inline const QList<Attribute> &attributes() const { return attrs; }
     inline const QString &preeditString() const { return preedit; }
@@ -435,6 +436,7 @@ public:
     inline const QString &commitString() const { return commit; }
     inline int replacementStart() const { return replace_from; }
     inline int replacementLength() const { return replace_length; }
+    inline const QString &tentativeCommitString() const { return tentativeCommit; }
 
     QInputMethodEvent(const QInputMethodEvent &other);
 
@@ -444,6 +446,7 @@ private:
     QString commit;
     int replace_from;
     int replace_length;
+    QString tentativeCommit;
 };
 
 class Q_GUI_EXPORT QInputMethodQueryEvent : public QEvent
index 1412bf5..a4a54ca 100644 (file)
@@ -1631,7 +1631,7 @@ QVariant QLineEdit::inputMethodQuery(Qt::InputMethodQuery property) const
     case Qt::ImCursorPosition:
         return QVariant(d->control->cursor());
     case Qt::ImSurroundingText:
-        return QVariant(text());
+        return QVariant(d->control->realText());
     case Qt::ImCurrentSelection:
         return QVariant(selectedText());
     case Qt::ImMaximumTextLength:
index 6869f56..498b972 100644 (file)
@@ -429,7 +429,7 @@ void QWidgetLineControl::moveCursor(int pos, bool mark)
 */
 void QWidgetLineControl::processInputMethodEvent(QInputMethodEvent *event)
 {
-    int priorState = 0;
+    int priorState = -1;
     bool isGettingInput = !event->commitString().isEmpty()
             || event->preeditString() != preeditAreaText()
             || event->replacementLength() > 0;
@@ -514,8 +514,16 @@ void QWidgetLineControl::processInputMethodEvent(QInputMethodEvent *event)
         emitCursorPositionChanged();
     else if (m_preeditCursor != oldPreeditCursor)
         emit updateMicroFocus();
-    if (isGettingInput)
+
+    bool tentativeCommitChanged = (m_tentativeCommit != event->tentativeCommitString());
+    if (tentativeCommitChanged) {
+        m_textDirty = true;
+        m_tentativeCommit = event->tentativeCommitString();
+    }
+
+    if (isGettingInput || tentativeCommitChanged)
         finishChange(priorState);
+
     if (selectionChange)
         emit selectionChanged();
 }
@@ -612,7 +620,6 @@ bool QWidgetLineControl::finishChange(int validateFromState, bool update, bool e
         m_validInput = true;
 #ifndef QT_NO_VALIDATOR
         if (m_validator) {
-            m_validInput = false;
             QString textCopy = m_text;
             int cursorCopy = m_cursor;
             m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
@@ -622,6 +629,15 @@ bool QWidgetLineControl::finishChange(int validateFromState, bool update, bool e
                     return true;
                 }
                 m_cursor = cursorCopy;
+
+                if (!m_tentativeCommit.isEmpty()) {
+                    textCopy.insert(m_cursor, m_tentativeCommit);
+                    bool validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+                    if (!validInput)
+                        m_tentativeCommit.clear();
+                }
+            } else {
+                m_tentativeCommit.clear();
             }
         }
 #endif
index 4ffa9e4..3bcdffb 100644 (file)
@@ -215,10 +215,26 @@ public:
 
     QString text() const
     {
+        QString content = m_text;
+        if (!m_tentativeCommit.isEmpty())
+            content.insert(m_cursor, m_tentativeCommit);
+        QString res = m_maskData ? stripString(content) : content;
+        return (res.isNull() ? QString::fromLatin1("") : res);
+    }
+    // like text() but doesn't include preedit
+    QString realText() const
+    {
         QString res = m_maskData ? stripString(m_text) : m_text;
         return (res.isNull() ? QString::fromLatin1("") : res);
     }
-    void setText(const QString &txt) { internalSetText(txt, -1, false); }
+    void setText(const QString &txt)
+    {
+        if (composeMode())
+            qApp->inputPanel()->reset();
+        m_tentativeCommit.clear();
+        internalSetText(txt, -1, false);
+    }
+
     QString displayText() const { return m_textLayout.text(); }
 
     void backspace();
@@ -379,6 +395,7 @@ private:
     int m_cursor;
     int m_preeditCursor;
     int m_cursorWidth;
+    QString m_tentativeCommit;
     Qt::LayoutDirection m_layoutDirection;
     uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
     uint m_separator : 1;
index 841fbbd..0804f12 100644 (file)
@@ -279,6 +279,7 @@ private slots:
 
     void selectAndCursorPosition();
     void inputMethodSelection();
+    void inputMethodTentativeCommit();
 
 protected slots:
     void editingFinished();
@@ -3831,5 +3832,30 @@ void tst_QLineEdit::inputMethodSelection()
     QCOMPARE(testWidget->selectionStart(), 12);
 }
 
+void tst_QLineEdit::inputMethodTentativeCommit()
+{
+    // test that basic tentative commit gets to text property on preedit state
+    QList<QInputMethodEvent::Attribute> attributes;
+    QInputMethodEvent event("test", attributes);
+    event.setTentativeCommitString("test");
+    QApplication::sendEvent(testWidget, &event);
+    QCOMPARE(testWidget->text(), QString("test"));
+
+    // tentative commit not allowed present in surrounding text
+    QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
+    QApplication::sendEvent(testWidget, &queryEvent);
+    QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
+
+    // if text with tentative commit does not validate, not allowed to be part of text property
+    testWidget->setText(""); // ensure input state is reset
+    QValidator *validator = new QIntValidator(0, 100);
+    testWidget->setValidator(validator);
+    QApplication::sendEvent(testWidget, &event);
+    QCOMPARE(testWidget->text(), QString(""));
+    testWidget->setValidator(0);
+    delete validator;
+}
+
+
 QTEST_MAIN(tst_QLineEdit)
 #include "tst_qlineedit.moc"