diff options
-rw-r--r-- | src/quick/items/qquicktextcontrol.cpp | 74 | ||||
-rw-r--r-- | src/quick/items/qquicktextcontrol_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquicktextcontrol_p_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquicktextedit.cpp | 12 | ||||
-rw-r--r-- | src/quick/items/qquicktextinput.cpp | 51 | ||||
-rw-r--r-- | src/quick/items/qquicktextinput_p_p.h | 5 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp | 118 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 117 |
8 files changed, 339 insertions, 46 deletions
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index eefe938467..739b5f859b 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -44,6 +44,7 @@ #ifndef QT_NO_TEXTCONTROL +#include <qcoreapplication.h> #include <qfont.h> #include <qpainter.h> #include <qevent.h> @@ -107,11 +108,12 @@ QQuickTextControlPrivate::QQuickTextControlPrivate() ignoreAutomaticScrollbarAdjustement(false), overwriteMode(false), acceptRichText(true), - hideCursor(false), + cursorVisible(false), hasFocus(false), isEnabled(true), hadSelectionOnMousePress(false), - wordSelectionEnabled(false) + wordSelectionEnabled(false), + hasImState(false) {} bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e) @@ -292,6 +294,8 @@ void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString & { Q_Q(QQuickTextControl); + cancelPreedit(); + // for use when called from setPlainText. we may want to re-use the currently // set char format then. const QTextCharFormat charFormatForInsertion = cursor.charFormat(); @@ -1441,13 +1445,16 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QList<QTextLayout::FormatRange> overrides; const int oldPreeditCursor = preeditCursor; preeditCursor = e->preeditString().length(); - hideCursor = false; + hasImState = !e->preeditString().isEmpty(); + cursorVisible = true; for (int i = 0; i < e->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = e->attributes().at(i); if (a.type == QInputMethodEvent::Cursor) { + hasImState = true; preeditCursor = a.start; - hideCursor = !a.length; + cursorVisible = a.length != 0; } else if (a.type == QInputMethodEvent::TextFormat) { + hasImState = true; QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); if (f.isValid()) { QTextLayout::FormatRange o; @@ -1514,6 +1521,37 @@ void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) } } +bool QQuickTextControl::hasImState() const +{ + Q_D(const QQuickTextControl); + return d->hasImState; +} + +bool QQuickTextControl::cursorVisible() const +{ + Q_D(const QQuickTextControl); + return d->cursorVisible; +} + +void QQuickTextControl::setCursorVisible(bool visible) +{ + Q_D(QQuickTextControl); + d->cursorVisible = visible; + d->setBlinkingCursorEnabled(d->cursorVisible + && (d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard))); +} + +QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const +{ + Q_D(const QQuickTextControl); + int cursorPos = hitTest(pos, Qt::FuzzyHit); + if (cursorPos == -1) + cursorPos = 0; + QTextCursor c(d->doc); + c.setPosition(cursorPos); + return c; +} + QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const { Q_D(const QQuickTextControl); @@ -1727,21 +1765,31 @@ bool QQuickTextControlPrivate::isPreediting() const void QQuickTextControlPrivate::commitPreedit() { - if (!isPreediting()) + Q_Q(QQuickTextControl); + + if (!hasImState) return; qApp->inputMethod()->commit(); - if (!isPreediting()) + if (!hasImState) return; - cursor.beginEditBlock(); - preeditCursor = 0; - QTextBlock block = cursor.block(); - QTextLayout *layout = block.layout(); - layout->setPreeditArea(-1, QString()); - layout->clearAdditionalFormats(); - cursor.endEditBlock(); + QInputMethodEvent event; + QCoreApplication::sendEvent(q->parent(), &event); +} + +void QQuickTextControlPrivate::cancelPreedit() +{ + Q_Q(QQuickTextControl); + + if (!hasImState) + return; + + qApp->inputMethod()->reset(); + + QInputMethodEvent event; + QCoreApplication::sendEvent(q->parent(), &event); } void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags) diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 9e3fc90eb1..be3f7f7ccf 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -98,6 +98,10 @@ public: QString toHtml() const; #endif + bool hasImState() const; + bool cursorVisible() const; + void setCursorVisible(bool visible); + QTextCursor cursorForPosition(const QPointF &pos) const; QRectF cursorRect(const QTextCursor &cursor) const; QRectF cursorRect() const; QRectF selectionRect(const QTextCursor &cursor) const; diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h index c5a39cc759..3a10f007be 100644 --- a/src/quick/items/qquicktextcontrol_p_p.h +++ b/src/quick/items/qquicktextcontrol_p_p.h @@ -127,6 +127,7 @@ public: bool isPreediting() const; void commitPreedit(); + void cancelPreedit(); QPointF trippleClickPoint; QPointF mousePressPos; @@ -155,11 +156,12 @@ public: bool ignoreAutomaticScrollbarAdjustement : 1; bool overwriteMode : 1; bool acceptRichText : 1; - bool hideCursor : 1; // used to hide the cursor in the preedit area + bool cursorVisible : 1; // used to hide the cursor in the preedit area bool hasFocus : 1; bool isEnabled : 1; bool hadSelectionOnMousePress : 1; bool wordSelectionEnabled : 1; + bool hasImState : 1; void _q_copyLink(); void _q_updateBlock(const QTextBlock &); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 4fa5233b9a..f727c54322 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -871,10 +871,9 @@ void QQuickTextEdit::setCursorVisible(bool on) if (d->cursorVisible == on) return; d->cursorVisible = on; - QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); if (!on && !d->persistentSelection) d->control->setCursorIsFocusIndicator(true); - d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); + d->control->setCursorVisible(on); emit cursorVisibleChanged(d->cursorVisible); } @@ -1536,15 +1535,18 @@ void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event) Q_D(QQuickTextEdit); const bool wasComposing = isInputMethodComposing(); d->control->processEvent(event, QPointF(0, -d->yoff)); + setCursorVisible(d->control->cursorVisible()); if (wasComposing != isInputMethodComposing()) emit inputMethodComposingChanged(); } void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) { + Q_D(QQuickTextEdit); if (change == ItemActiveFocusHasChanged) { setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus()); - + QFocusEvent focusEvent(value.boolValue ? QEvent::FocusIn : QEvent::FocusOut); + d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); if (value.boolValue) { q_updateAlignment(); connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), @@ -1729,9 +1731,7 @@ bool QQuickTextEdit::canRedo() const bool QQuickTextEdit::isInputMethodComposing() const { Q_D(const QQuickTextEdit); - if (QTextLayout *layout = d->control->textCursor().block().layout()) - return layout->preeditAreaText().length() > 0; - return false; + return d->control->hasImState(); } void QQuickTextEditPrivate::init() diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index ccd7ff1673..5aa01d27f3 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -45,6 +45,7 @@ #include <private/qqmlglobal_p.h> +#include <QtCore/qcoreapplication.h> #include <QtQml/qqmlinfo.h> #include <QtGui/qevent.h> #include <QTextBoundaryFinder> @@ -124,8 +125,8 @@ void QQuickTextInput::setText(const QString &s) Q_D(QQuickTextInput); if (s == text()) return; - if (d->composeMode()) - qApp->inputMethod()->reset(); + + d->cancelPreedit(); d->internalSetText(s, -1, false); } @@ -674,9 +675,7 @@ QRectF QQuickTextInput::cursorRectangle() const { Q_D(const QQuickTextInput); - int c = d->m_cursor; - if (d->m_preeditCursor != -1) - c += d->m_preeditCursor; + int c = d->m_cursor + d->m_preeditCursor; if (d->m_echoMode == NoEcho) c = 0; QTextLine l = d->m_textLayout.lineForTextPosition(c); @@ -1398,7 +1397,7 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev) void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) { Q_D(QQuickTextInput); - const bool wasComposing = d->preeditAreaText().length() > 0; + const bool wasComposing = d->hasImState; if (d->m_readOnly) { ev->ignore(); } else { @@ -1407,7 +1406,7 @@ void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) if (!ev->isAccepted()) QQuickImplicitSizeItem::inputMethodEvent(ev); - if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0)) + if (wasComposing != d->hasImState) emit inputMethodComposingChanged(); } @@ -2513,7 +2512,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) bool QQuickTextInput::isInputMethodComposing() const { Q_D(const QQuickTextInput); - return d->preeditAreaText().length() > 0; + return d->hasImState; } void QQuickTextInputPrivate::init() @@ -2820,18 +2819,31 @@ void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode) */ void QQuickTextInputPrivate::commitPreedit() { - if (!composeMode()) + Q_Q(QQuickTextInput); + + if (!hasImState) return; qApp->inputMethod()->commit(); - if (!composeMode()) + if (!hasImState) + return; + + QInputMethodEvent ev; + QCoreApplication::sendEvent(q, &ev); +} + +void QQuickTextInputPrivate::cancelPreedit() +{ + Q_Q(QQuickTextInput); + + if (!hasImState) return; - m_preeditCursor = 0; - m_textLayout.setPreeditArea(-1, QString()); - m_textLayout.clearAdditionalFormats(); - updateLayout(); + qApp->inputMethod()->reset(); + + QInputMethodEvent ev; + QCoreApplication::sendEvent(q, &ev); } /*! @@ -3113,15 +3125,19 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) m_textLayout.setPreeditArea(m_cursor, event->preeditString()); #endif //QT_NO_IM const int oldPreeditCursor = m_preeditCursor; + const bool oldCursorVisible = cursorVisible; m_preeditCursor = event->preeditString().length(); - m_hideCursor = false; + hasImState = !event->preeditString().isEmpty(); + cursorVisible = true; QList<QTextLayout::FormatRange> formats; for (int i = 0; i < event->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = event->attributes().at(i); if (a.type == QInputMethodEvent::Cursor) { + hasImState = true; m_preeditCursor = a.start; - m_hideCursor = !a.length; + cursorVisible = a.length != 0; } else if (a.type == QInputMethodEvent::TextFormat) { + hasImState = true; QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); if (f.isValid()) { QTextLayout::FormatRange o; @@ -3144,6 +3160,9 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) if (isGettingInput) finishChange(priorState); + if (cursorVisible != oldCursorVisible) + emit q->cursorVisibleChanged(cursorVisible); + if (selectionChange) { emit q->selectionChanged(); q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 74efdcf469..1bc2cf548b 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -115,7 +115,7 @@ public: , selectPressed(false) , textLayoutDirty(true) , persistentSelection(false) - , m_hideCursor(false) + , hasImState(false) , m_separator(0) , m_readOnly(0) , m_textDirty(0) @@ -245,7 +245,7 @@ public: bool selectPressed:1; bool textLayoutDirty:1; bool persistentSelection:1; - bool m_hideCursor : 1; // used to hide the m_cursor inside preedit areas + bool hasImState : 1; bool m_separator : 1; bool m_readOnly : 1; bool m_textDirty : 1; @@ -319,6 +319,7 @@ public: #endif void commitPreedit(); + void cancelPreedit(); Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 9173a2b177..dd9aa0acad 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -1999,15 +1999,16 @@ void tst_qquicktextedit::cursorDelegate() void tst_qquicktextedit::cursorVisible() { + QQuickTextEdit edit; + edit.componentComplete(); + QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool))); + QQuickView view(testFileUrl("cursorVisible.qml")); view.show(); view.requestActivateWindow(); QTest::qWaitForWindowShown(&view); QTRY_COMPARE(&view, qGuiApp->focusWindow()); - QQuickTextEdit edit; - QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool))); - QCOMPARE(edit.isCursorVisible(), false); edit.setCursorVisible(true); @@ -2034,7 +2035,7 @@ void tst_qquicktextedit::cursorVisible() QCOMPARE(edit.isCursorVisible(), true); QCOMPARE(spy.count(), 5); - QQuickView alternateView; + QWindow alternateView; alternateView.show(); alternateView.requestActivateWindow(); QTest::qWaitForWindowShown(&alternateView); @@ -2046,6 +2047,47 @@ void tst_qquicktextedit::cursorVisible() QTest::qWaitForWindowShown(&view); QCOMPARE(edit.isCursorVisible(), true); QCOMPARE(spy.count(), 7); + + { // Cursor attribute with 0 length hides cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 8); + + { // Cursor attribute with non zero length shows cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 9); + + + { // If the cursor is hidden by the input method and the text is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 10); + + edit.setText("something"); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 11); + + { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 12); + + edit.setCursorPosition(5); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 13); } void tst_qquicktextedit::delegateLoading_data() @@ -2682,6 +2724,74 @@ void tst_qquicktextedit::inputMethodComposing() } QCOMPARE(edit->isInputMethodComposing(), false); QCOMPARE(spy.count(), 2); + + // Changing the text while not composing doesn't alter the composing state. + edit->setText(text.mid(0, 16)); + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); + + { + QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 3); + + // Changing the text while composing cancels composition. + edit->setText(text.mid(0, 12)); + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 4); + + { // Preedit cursor positioned outside (empty) preedit; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Cursor hidden; composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Default cursor attributes; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Selections are persisted: not composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 2, 4, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 6); + + edit->setCursorPosition(0); + + { // Formatting applied; composing. + QTextCharFormat format; + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 2, 4, format)); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 7); + + { + QInputMethodEvent event; + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 8); } void tst_qquicktextedit::cursorRectangleSize() diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 1c443801f5..11029515e2 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -2366,16 +2366,16 @@ void tst_qquicktextinput::cursorDelegate() void tst_qquicktextinput::cursorVisible() { + QQuickTextInput input; + input.componentComplete(); + QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool))); + QQuickView view(testFileUrl("cursorVisible.qml")); view.show(); view.requestActivateWindow(); QTest::qWaitForWindowShown(&view); QTRY_COMPARE(&view, qGuiApp->focusWindow()); - QQuickTextInput input; - input.componentComplete(); - QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool))); - QCOMPARE(input.isCursorVisible(), false); input.setCursorVisible(true); @@ -2414,6 +2414,46 @@ void tst_qquicktextinput::cursorVisible() QTest::qWaitForWindowShown(&view); QCOMPARE(input.isCursorVisible(), true); QCOMPARE(spy.count(), 7); + + { // Cursor attribute with 0 length hides cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 8); + + { // Cursor attribute with non zero length shows cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 9); + + { // If the cursor is hidden by the input method and the text is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 10); + + input.setText("something"); + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 11); + + { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 12); + + input.setCursorPosition(5); + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 13); } void tst_qquicktextinput::cursorRectangle_data() @@ -3280,6 +3320,75 @@ void tst_qquicktextinput::inputMethodComposing() } QCOMPARE(input->isInputMethodComposing(), false); QCOMPARE(spy.count(), 2); + + // Changing the text while not composing doesn't alter the composing state. + input->setText(text.mid(0, 16)); + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); + + { + QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 3); + + // Changing the text while composing cancels composition. + input->setText(text.mid(0, 12)); + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 4); + + { // Preedit cursor positioned outside (empty) preedit; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + + { // Cursor hidden; composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Default cursor attributes; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Selections are persisted: not composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 6); + + input->setCursorPosition(12); + + { // Formatting applied; composing. + QTextCharFormat format; + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format)); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 7); + + { + QInputMethodEvent event; + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 8); } void tst_qquicktextinput::inputMethodUpdate() |