diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index ba81e5512c..2c0858ae00 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -1643,6 +1643,17 @@ void QInputMethodEvent::setCommitString(const QString &commitString, int replace replace_length = replaceLength; } +/*! + 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 &QInputMethodEvent::attributes() const @@ -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 diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index de888832bf..aad31a90eb 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -428,6 +428,7 @@ public: QInputMethodEvent(); QInputMethodEvent(const QString &preeditText, const QList &attributes); void setCommitString(const QString &commitString, int replaceFrom = 0, int replaceLength = 0); + void setTentativeCommitString(const QString &tentativeCommitString); inline const QList &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 diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 1412bf547a..a4a54caa53 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -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: diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index 6869f56e0a..498b972045 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -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 diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h index 4ffa9e4b31..3bcdffbf08 100644 --- a/src/widgets/widgets/qwidgetlinecontrol_p.h +++ b/src/widgets/widgets/qwidgetlinecontrol_p.h @@ -214,11 +214,27 @@ public: void setReadOnly(bool enable) { m_readOnly = enable; } 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; diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index 841fbbd955..0804f1241c 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -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 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"