Add QQuickTextEdit::append()
authorJ-P Nurmi <jpnurmi@digia.com>
Thu, 6 Jun 2013 13:16:59 +0000 (15:16 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 11 Jun 2013 22:18:15 +0000 (00:18 +0200)
This makes it possible to append text more efficiently than
appending to the text -property, and also avoids weird rich
text formatting issues with the latter approach.

Task-number: QTBUG-31575
Change-Id: Id621773588b94e36f8f0b9eb6b22590e9db62811
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
Reviewed-by: Pierre Rossi <pierre.rossi@gmail.com>
src/quick/items/qquicktextedit.cpp
src/quick/items/qquicktextedit_p.h
tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp

index 6b85190..92e81e0 100644 (file)
@@ -272,6 +272,12 @@ QString QQuickTextEdit::text() const
     The text to display.  If the text format is AutoText the text edit will
     automatically determine whether the text should be treated as
     rich text.  This determination is made using Qt::mightBeRichText().
+
+    The text-property is mostly suitable for setting the initial content and
+    handling modifications to relatively small text content. The append(),
+    insert() and remove() methods provide more fine-grained control and
+    remarkably better performance for modifying especially large rich text
+    content.
 */
 void QQuickTextEdit::setText(const QString &text)
 {
@@ -2533,4 +2539,37 @@ void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
         d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
 }
 
+/*!
+    \qmlmethod void QtQuick2::TextEdit::append(string text)
+    \since QtQuick 2.2
+
+    Appends a new paragraph with \a text to the end of the TextEdit.
+
+    In order to append without inserting a new paragraph,
+    call \c myTextEdit.insert(myTextEdit.length, text) instead.
+*/
+void QQuickTextEdit::append(const QString &text)
+{
+    Q_D(QQuickTextEdit);
+    QTextCursor cursor(d->document);
+    cursor.beginEditBlock();
+    cursor.movePosition(QTextCursor::End);
+
+    if (!d->document->isEmpty())
+        cursor.insertBlock();
+
+#ifndef QT_NO_TEXTHTMLPARSER
+    if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
+        cursor.insertHtml(text);
+    } else {
+        cursor.insertText(text);
+    }
+#else
+    cursor.insertText(text);
+#endif // QT_NO_TEXTHTMLPARSER
+
+    cursor.endEditBlock();
+    d->control->updateCursorRectangle(false);
+}
+
 QT_END_NAMESPACE
index 1e03864..69ffad7 100644 (file)
@@ -314,6 +314,7 @@ public Q_SLOTS:
     void redo();
     void insert(int position, const QString &text);
     void remove(int start, int end);
+    Q_REVISION(2) void append(const QString &text);
 
 private Q_SLOTS:
     void q_textChanged();
index 80e8335..798db9f 100644 (file)
@@ -182,6 +182,8 @@ private slots:
     void getText();
     void getFormattedText_data();
     void getFormattedText();
+    void append_data();
+    void append();
     void insert_data();
     void insert();
     void remove_data();
@@ -3827,6 +3829,158 @@ void tst_qquicktextedit::getFormattedText()
     }
 }
 
+void tst_qquicktextedit::append_data()
+{
+    QTest::addColumn<QString>("text");
+    QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
+    QTest::addColumn<int>("selectionStart");
+    QTest::addColumn<int>("selectionEnd");
+    QTest::addColumn<QString>("appendText");
+    QTest::addColumn<QString>("expectedText");
+    QTest::addColumn<int>("expectedSelectionStart");
+    QTest::addColumn<int>("expectedSelectionEnd");
+    QTest::addColumn<int>("expectedCursorPosition");
+    QTest::addColumn<bool>("selectionChanged");
+    QTest::addColumn<bool>("cursorPositionChanged");
+
+    QTest::newRow("cursor kept intact (beginning)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 0 << 0
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 0 << 0 << 0
+            << false << false;
+
+    QTest::newRow("cursor kept intact (middle)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 18 << 18
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 18 << 18 << 18
+            << false << false;
+
+    QTest::newRow("cursor follows (end)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << standard.at(0).length() << standard.at(0).length()
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << standard.at(0).length() + 6 << standard.at(0).length() + 6 << standard.at(0).length() + 6
+            << false << true;
+
+    QTest::newRow("selection kept intact (beginning)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 0 << 18
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 0 << 18 << 18
+            << false << false;
+
+    QTest::newRow("selection kept intact (middle)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 14 << 18
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 14 << 18 << 18
+            << false << false;
+
+    QTest::newRow("selection kept intact, cursor follows (end)")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 18 << standard.at(0).length()
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 18 << standard.at(0).length() + 6 << standard.at(0).length() + 6
+            << false << true;
+
+    QTest::newRow("reversed selection kept intact")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 18 << 14
+            << QString("Hello")
+            << standard.at(0) + QString("\nHello")
+            << 14 << 18 << 14
+            << false << false;
+
+    QTest::newRow("rich text into plain text")
+            << standard.at(0) << QQuickTextEdit::PlainText
+            << 0 << 0
+            << QString("<b>Hello</b>")
+            << standard.at(0) + QString("\n<b>Hello</b>")
+            << 0 << 0 << 0
+            << false << false;
+
+    QTest::newRow("rich text into rich text")
+            << standard.at(0) << QQuickTextEdit::RichText
+            << 0 << 0
+            << QString("<b>Hello</b>")
+            << standard.at(0) + QChar(QChar::ParagraphSeparator) + QString("Hello")
+            << 0 << 0 << 0
+            << false << false;
+
+    QTest::newRow("rich text into auto text")
+            << standard.at(0) << QQuickTextEdit::AutoText
+            << 0 << 0
+            << QString("<b>Hello</b>")
+            << standard.at(0) + QString("\nHello")
+            << 0 << 0 << 0
+            << false << false;
+}
+
+void tst_qquicktextedit::append()
+{
+    QFETCH(QString, text);
+    QFETCH(QQuickTextEdit::TextFormat, textFormat);
+    QFETCH(int, selectionStart);
+    QFETCH(int, selectionEnd);
+    QFETCH(QString, appendText);
+    QFETCH(QString, expectedText);
+    QFETCH(int, expectedSelectionStart);
+    QFETCH(int, expectedSelectionEnd);
+    QFETCH(int, expectedCursorPosition);
+    QFETCH(bool, selectionChanged);
+    QFETCH(bool, cursorPositionChanged);
+
+    QString componentStr = "import QtQuick 2.2\nTextEdit { text: \"" + text + "\" }";
+    QQmlComponent textEditComponent(&engine);
+    textEditComponent.setData(componentStr.toLatin1(), QUrl());
+    QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
+    QVERIFY(textEdit != 0);
+
+    textEdit->setTextFormat(textFormat);
+    textEdit->select(selectionStart, selectionEnd);
+
+    QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
+    QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
+    QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
+    QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
+    QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
+
+    textEdit->append(appendText);
+
+    if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
+            Qt::mightBeRichText(text) || Qt::mightBeRichText(appendText)))) {
+        QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
+    } else {
+        QCOMPARE(textEdit->text(), expectedText);
+
+    }
+    QCOMPARE(textEdit->length(), expectedText.length());
+
+    QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
+    QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
+    QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
+
+    if (selectionStart > selectionEnd)
+        qSwap(selectionStart, selectionEnd);
+
+    QEXPECT_FAIL("into selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
+    QEXPECT_FAIL("into reversed selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
+    QCOMPARE(selectionSpy.count() > 0, selectionChanged);
+    QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
+    QEXPECT_FAIL("into reversed selection", "selectionEndChanged signal not emitted", Continue);
+    QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
+    QCOMPARE(textSpy.count() > 0, text != expectedText);
+    QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
+}
+
 void tst_qquicktextedit::insert_data()
 {
     QTest::addColumn<QString>("text");