1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/testhttpserver.h"
46 #include <QTextDocument>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlcontext.h>
49 #include <QtQml/qqmlexpression.h>
50 #include <QtQml/qqmlcomponent.h>
51 #include <QtGui/qguiapplication.h>
52 #include <private/qquicktextedit_p.h>
53 #include <private/qquicktextedit_p_p.h>
54 #include <private/qquicktext_p_p.h>
55 #include <QFontMetrics>
56 #include <QtQuick/QQuickView>
58 #include <QInputMethod>
61 #include <private/qquicktextcontrol_p.h>
62 #include "../../shared/util.h"
63 #include "../../shared/platforminputcontext.h"
64 #include <private/qinputmethod_p.h>
67 #include <Carbon/Carbon.h>
71 Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode)
72 Q_DECLARE_METATYPE(Qt::Key)
73 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
75 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
77 // XXX This will be replaced by some clever persistent platform image store.
78 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
79 QString arch = "unknown-architecture"; // QTest needs to help with this.
81 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
83 if (!QFile::exists(expectfile)) {
84 actual.save(expectfile);
85 qWarning() << "created" << expectfile;
91 typedef QPair<int, QChar> Key;
93 class tst_qquicktextedit : public QQmlDataTest
107 void alignments_data();
109 // ### these tests may be trivial
111 void hAlign_RightToLeft();
117 void persistentSelection();
120 void isRightToLeft_data();
121 void isRightToLeft();
123 void moveCursorSelection_data();
124 void moveCursorSelection();
125 void moveCursorSelectionSequence_data();
126 void moveCursorSelectionSequence();
127 void mouseSelection_data();
128 void mouseSelection();
129 void mouseSelectionMode_data();
130 void mouseSelectionMode();
131 void dragMouseSelection();
132 void inputMethodHints();
136 void linkActivated();
138 void cursorDelegate_data();
139 void cursorDelegate();
140 void cursorVisible();
141 void delegateLoading_data();
142 void delegateLoading();
147 void canPasteEmpty();
149 void inputMethodUpdate();
150 void openInputPanel();
151 void geometrySignals();
152 void pastingRichText_QTBUG_14003();
153 void implicitSize_data();
158 void implicitSizeBinding_data();
159 void implicitSizeBinding();
161 void preeditCursorRectangle();
162 void inputMethodComposing();
163 void cursorRectangleSize();
167 void getFormattedText_data();
168 void getFormattedText();
174 void keySequence_data();
181 void undo_keypressevents_data();
182 void undo_keypressevents();
185 void embeddedImages();
186 void embeddedImages_data();
188 void emptytags_QTBUG_22058();
191 void simulateKeys(QWindow *window, const QList<Key> &keys);
192 void simulateKeys(QWindow *window, const QKeySequence &sequence);
194 void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = 0);
196 QStringList standard;
197 QStringList richText;
199 QStringList hAlignmentStrings;
200 QStringList vAlignmentStrings;
202 QList<Qt::Alignment> vAlignments;
203 QList<Qt::Alignment> hAlignments;
205 QStringList colorStrings;
210 typedef QList<int> IntList;
211 Q_DECLARE_METATYPE(IntList)
213 typedef QList<Key> KeyList;
214 Q_DECLARE_METATYPE(KeyList)
216 Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
218 void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
220 for (int i = 0; i < keys.count(); ++i) {
221 const int key = keys.at(i).first;
222 const int modifiers = key & Qt::KeyboardModifierMask;
223 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
225 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
226 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
228 QGuiApplication::sendEvent(window, &press);
229 QGuiApplication::sendEvent(window, &release);
233 void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &sequence)
235 for (int i = 0; i < sequence.count(); ++i) {
236 const int key = sequence[i];
237 const int modifiers = key & Qt::KeyboardModifierMask;
239 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
243 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
245 for (int i = 0; i < sequence.count(); ++i)
246 keys << Key(sequence[i], QChar());
250 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
252 for (int i = 0; i < N - 1; ++i) {
253 int key = QTest::asciiToKey(characters[i]);
254 QChar character = QLatin1Char(characters[i]);
255 keys << Key(key, character);
260 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
262 keys << Key(key, QChar());
266 tst_qquicktextedit::tst_qquicktextedit()
268 standard << "the quick brown fox jumped over the lazy dog"
269 << "the quick brown fox\n jumped over the lazy dog"
273 richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
274 << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
276 hAlignmentStrings << "AlignLeft"
280 vAlignmentStrings << "AlignTop"
284 hAlignments << Qt::AlignLeft
288 vAlignments << Qt::AlignTop
292 colorStrings << "aliceblue"
305 // need a different test to do alpha channel test
311 void tst_qquicktextedit::cleanup()
313 // ensure not even skipped tests with custom input context leave it dangling
314 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
315 inputMethodPrivate->testContext = 0;
318 void tst_qquicktextedit::text()
321 QQmlComponent texteditComponent(&engine);
322 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
323 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
325 QVERIFY(textEditObject != 0);
326 QCOMPARE(textEditObject->text(), QString(""));
327 QCOMPARE(textEditObject->length(), 0);
330 for (int i = 0; i < standard.size(); i++)
332 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
333 QQmlComponent texteditComponent(&engine);
334 texteditComponent.setData(componentStr.toLatin1(), QUrl());
335 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
337 QVERIFY(textEditObject != 0);
338 QCOMPARE(textEditObject->text(), standard.at(i));
339 QCOMPARE(textEditObject->length(), standard.at(i).length());
342 for (int i = 0; i < richText.size(); i++)
344 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
345 QQmlComponent texteditComponent(&engine);
346 texteditComponent.setData(componentStr.toLatin1(), QUrl());
348 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
350 QVERIFY(textEditObject != 0);
352 QString expected = richText.at(i);
353 expected.replace(QRegExp("\\\\(.)"),"\\1");
354 QCOMPARE(textEditObject->text(), expected);
355 QCOMPARE(textEditObject->length(), expected.length());
358 for (int i = 0; i < standard.size(); i++)
360 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + standard.at(i) + "\" }";
361 QQmlComponent texteditComponent(&engine);
362 texteditComponent.setData(componentStr.toLatin1(), QUrl());
363 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
365 QVERIFY(textEditObject != 0);
367 QString actual = textEditObject->text();
368 QString expected = standard.at(i);
369 actual.remove(QRegExp(".*<body[^>]*>"));
370 actual.remove(QRegExp("(<[^>]*>)+"));
371 expected.remove("\n");
372 QCOMPARE(actual.simplified(), expected);
373 QCOMPARE(textEditObject->length(), expected.length());
376 for (int i = 0; i < richText.size(); i++)
378 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
379 QQmlComponent texteditComponent(&engine);
380 texteditComponent.setData(componentStr.toLatin1(), QUrl());
381 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
383 QVERIFY(textEditObject != 0);
384 QString actual = textEditObject->text();
385 QString expected = richText.at(i);
386 actual.replace(QRegExp(".*<body[^>]*>"),"");
387 actual.replace(QRegExp("(<[^>]*>)+"),"<>");
388 expected.replace(QRegExp("(<[^>]*>)+"),"<>");
389 QCOMPARE(actual.simplified(),expected.simplified());
391 expected.replace("<>", " ");
392 QCOMPARE(textEditObject->length(), expected.simplified().length());
395 for (int i = 0; i < standard.size(); i++)
397 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + standard.at(i) + "\" }";
398 QQmlComponent texteditComponent(&engine);
399 texteditComponent.setData(componentStr.toLatin1(), QUrl());
400 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
402 QVERIFY(textEditObject != 0);
403 QCOMPARE(textEditObject->text(), standard.at(i));
404 QCOMPARE(textEditObject->length(), standard.at(i).length());
407 for (int i = 0; i < richText.size(); i++)
409 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + richText.at(i) + "\" }";
410 QQmlComponent texteditComponent(&engine);
411 texteditComponent.setData(componentStr.toLatin1(), QUrl());
412 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
414 QVERIFY(textEditObject != 0);
415 QString actual = textEditObject->text();
416 QString expected = richText.at(i);
417 actual.replace(QRegExp(".*<body[^>]*>"),"");
418 actual.replace(QRegExp("(<[^>]*>)+"),"<>");
419 expected.replace(QRegExp("(<[^>]*>)+"),"<>");
420 QCOMPARE(actual.simplified(),expected.simplified());
422 expected.replace("<>", " ");
423 QCOMPARE(textEditObject->length(), expected.simplified().length());
427 void tst_qquicktextedit::width()
429 // uses Font metrics to find the width for standard and document to find the width for rich
431 QQmlComponent texteditComponent(&engine);
432 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
433 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
435 QVERIFY(textEditObject != 0);
436 QCOMPARE(textEditObject->width(), 0.0);
439 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
441 for (int i = 0; i < standard.size(); i++)
443 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
444 QQmlComponent texteditComponent(&engine);
445 texteditComponent.setData(componentStr.toLatin1(), QUrl());
446 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
448 QString s = standard.at(i);
449 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
451 QTextLayout layout(s);
452 layout.setFont(textEditObject->font());
453 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
454 if (requiresUnhintedMetrics) {
456 option.setUseDesignMetrics(true);
457 layout.setTextOption(option);
460 layout.beginLayout();
462 QTextLine line = layout.createLine();
469 qreal metricWidth = layout.boundingRect().width();
471 QVERIFY(textEditObject != 0);
472 QCOMPARE(textEditObject->width(), metricWidth);
475 for (int i = 0; i < richText.size(); i++)
477 QTextDocument document;
478 document.setHtml(richText.at(i));
479 document.setDocumentMargin(0);
480 if (requiresUnhintedMetrics)
481 document.setUseDesignMetrics(true);
483 qreal documentWidth = document.idealWidth();
485 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
486 QQmlComponent texteditComponent(&engine);
487 texteditComponent.setData(componentStr.toLatin1(), QUrl());
488 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
490 QVERIFY(textEditObject != 0);
491 QCOMPARE(textEditObject->width(), documentWidth);
495 void tst_qquicktextedit::wrap()
497 // for specified width and wrap set true
499 QQmlComponent texteditComponent(&engine);
500 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
501 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
503 QVERIFY(textEditObject != 0);
504 QCOMPARE(textEditObject->width(), 300.);
507 for (int i = 0; i < standard.size(); i++)
509 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
510 QQmlComponent texteditComponent(&engine);
511 texteditComponent.setData(componentStr.toLatin1(), QUrl());
512 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
514 QVERIFY(textEditObject != 0);
515 QCOMPARE(textEditObject->width(), 300.);
518 for (int i = 0; i < richText.size(); i++)
520 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
521 QQmlComponent texteditComponent(&engine);
522 texteditComponent.setData(componentStr.toLatin1(), QUrl());
523 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
525 QVERIFY(textEditObject != 0);
526 QCOMPARE(textEditObject->width(), 300.);
531 void tst_qquicktextedit::textFormat()
534 QQmlComponent textComponent(&engine);
535 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
536 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
538 QVERIFY(textObject != 0);
539 QVERIFY(textObject->textFormat() == QQuickTextEdit::RichText);
542 QQmlComponent textComponent(&engine);
543 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
544 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
546 QVERIFY(textObject != 0);
547 QVERIFY(textObject->textFormat() == QQuickTextEdit::PlainText);
551 void tst_qquicktextedit::alignments_data()
553 QTest::addColumn<int>("hAlign");
554 QTest::addColumn<int>("vAlign");
555 QTest::addColumn<QString>("expectfile");
557 QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
558 QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
559 QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
561 QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
562 QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
563 QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
565 QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
566 QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
567 QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
571 void tst_qquicktextedit::alignments()
573 QSKIP("Image comparison of text is almost guaranteed to fail during development");
577 QFETCH(QString, expectfile);
579 QQuickView canvas(testFileUrl("alignments.qml"));
582 canvas.requestActivateWindow();
583 QTest::qWaitForWindowShown(&canvas);
584 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
586 QObject *ob = canvas.rootObject();
588 ob->setProperty("horizontalAlignment",hAlign);
589 ob->setProperty("verticalAlignment",vAlign);
590 QTRY_COMPARE(ob->property("running").toBool(),false);
591 QImage actual = canvas.grabFrameBuffer();
593 expectfile = createExpectedFileIfNotFound(expectfile, actual);
595 QImage expect(expectfile);
597 QCOMPARE(actual,expect);
601 //the alignment tests may be trivial o.oa
602 void tst_qquicktextedit::hAlign()
604 //test one align each, and then test if two align fails.
606 for (int i = 0; i < standard.size(); i++)
608 for (int j=0; j < hAlignmentStrings.size(); j++)
610 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
611 QQmlComponent texteditComponent(&engine);
612 texteditComponent.setData(componentStr.toLatin1(), QUrl());
613 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
615 QVERIFY(textEditObject != 0);
616 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
620 for (int i = 0; i < richText.size(); i++)
622 for (int j=0; j < hAlignmentStrings.size(); j++)
624 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
625 QQmlComponent texteditComponent(&engine);
626 texteditComponent.setData(componentStr.toLatin1(), QUrl());
627 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
629 QVERIFY(textEditObject != 0);
630 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
636 void tst_qquicktextedit::hAlign_RightToLeft()
638 PlatformInputContext platformInputContext;
639 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
640 inputMethodPrivate->testContext = &platformInputContext;
642 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
643 QQuickTextEdit *textEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("text");
644 QVERIFY(textEdit != 0);
647 const QString rtlText = textEdit->text();
649 // implicit alignment should follow the reading direction of text
650 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
651 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
653 // explicitly left aligned
654 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
655 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
656 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
658 // explicitly right aligned
659 textEdit->setHAlign(QQuickTextEdit::AlignRight);
660 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
661 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
663 QString textString = textEdit->text();
664 textEdit->setText(QString("<i>") + textString + QString("</i>"));
665 textEdit->resetHAlign();
667 // implicitly aligned rich text should follow the reading direction of RTL text
668 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
669 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
670 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
672 // explicitly left aligned rich text
673 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
674 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
675 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
676 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
678 // explicitly right aligned rich text
679 textEdit->setHAlign(QQuickTextEdit::AlignRight);
680 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
681 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
682 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
684 textEdit->setText(textString);
686 // explicitly center aligned
687 textEdit->setHAlign(QQuickTextEdit::AlignHCenter);
688 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignHCenter);
689 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
691 // reseted alignment should go back to following the text reading direction
692 textEdit->resetHAlign();
693 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
694 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
696 // mirror the text item
697 QQuickItemPrivate::get(textEdit)->setLayoutMirror(true);
699 // mirrored implicit alignment should continue to follow the reading direction of the text
700 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
701 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
702 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
704 // mirrored explicitly right aligned behaves as left aligned
705 textEdit->setHAlign(QQuickTextEdit::AlignRight);
706 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
707 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
708 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
710 // mirrored explicitly left aligned behaves as right aligned
711 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
712 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
713 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
714 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
717 QQuickItemPrivate::get(textEdit)->setLayoutMirror(false);
718 textEdit->resetHAlign();
720 // English text should be implicitly left aligned
721 textEdit->setText("Hello world!");
722 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
723 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
725 canvas.requestActivateWindow();
726 QTest::qWaitForWindowShown(&canvas);
727 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
729 textEdit->setText(QString());
730 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
731 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
732 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
733 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
735 // Clear pre-edit text. TextEdit should maybe do this itself on setText, but that may be
736 // redundant as an actual input method may take care of it.
737 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
739 // empty text with implicit alignment follows the system locale-based
740 // keyboard input direction from qApp->inputMethod()->inputDirection
741 textEdit->setText("");
742 platformInputContext.setInputDirection(Qt::LeftToRight);
743 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
744 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
745 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
747 QSignalSpy cursorRectangleSpy(textEdit, SIGNAL(cursorRectangleChanged()));
749 platformInputContext.setInputDirection(Qt::RightToLeft);
750 QCOMPARE(cursorRectangleSpy.count(), 1);
751 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
752 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
753 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
755 // neutral text follows also input method direction
756 textEdit->resetHAlign();
757 textEdit->setText(" ()((=<>");
758 platformInputContext.setInputDirection(Qt::LeftToRight);
759 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
760 QVERIFY(textEdit->cursorRectangle().left() < canvas.width()/2);
761 platformInputContext.setInputDirection(Qt::RightToLeft);
762 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
763 QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
765 // set input direction while having content
766 platformInputContext.setInputDirection(Qt::LeftToRight);
767 textEdit->setText("a");
768 textEdit->setCursorPosition(1);
769 platformInputContext.setInputDirection(Qt::RightToLeft);
770 QTest::keyClick(&canvas, Qt::Key_Backspace);
771 QVERIFY(textEdit->text().isEmpty());
772 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
773 QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
775 // input direction changed while not having focus
776 platformInputContext.setInputDirection(Qt::LeftToRight);
777 textEdit->setFocus(false);
778 platformInputContext.setInputDirection(Qt::RightToLeft);
779 textEdit->setFocus(true);
780 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
781 QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
783 textEdit->setHAlign(QQuickTextEdit::AlignRight);
784 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
785 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
787 // make sure editor doesn't rely on input for updating size
788 QQuickTextEdit *emptyEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("emptyTextEdit");
789 QVERIFY(emptyEdit != 0);
790 platformInputContext.setInputDirection(Qt::RightToLeft);
791 emptyEdit->setFocus(true);
792 QCOMPARE(emptyEdit->hAlign(), QQuickTextEdit::AlignRight);
793 QVERIFY(emptyEdit->cursorRectangle().left() > canvas.width()/2);
797 static int numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
800 for (int x = fromX; x < toX; ++x) {
801 for (int y = 0; y < image.height(); ++y) {
802 if (image.pixel(x, y) != qRgb(255, 255, 255))
809 void tst_qquicktextedit::hAlignVisual()
811 QQuickView view(testFileUrl("hAlignVisual.qml"));
813 view.requestActivateWindow();
814 QTest::qWaitForWindowShown(&view);
816 QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
820 QImage image = view.grabFrameBuffer();
821 int left = numberOfNonWhitePixels(0, image.width() / 3, image);
822 int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
823 int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
825 QVERIFY(mid > right);
829 text->setHAlign(QQuickText::AlignHCenter);
830 QImage image = view.grabFrameBuffer();
831 int left = numberOfNonWhitePixels(0, image.width() / 3, image);
832 int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
833 int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
835 QVERIFY(mid > right);
839 text->setHAlign(QQuickText::AlignRight);
840 QImage image = view.grabFrameBuffer();
841 int left = numberOfNonWhitePixels(0, image.width() / 3, image);
842 int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
843 int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
845 QVERIFY(mid < right);
852 QImage image = view.grabFrameBuffer();
853 int x = qCeil(text->implicitWidth());
854 int left = numberOfNonWhitePixels(0, x, image);
855 int right = numberOfNonWhitePixels(x, image.width() - x, image);
861 text->setHAlign(QQuickText::AlignHCenter);
862 QImage image = view.grabFrameBuffer();
863 int x1 = qFloor(image.width() - text->implicitWidth()) / 2;
864 int x2 = image.width() - x1;
865 int left = numberOfNonWhitePixels(0, x1, image);
866 int mid = numberOfNonWhitePixels(x1, x2 - x1, image);
867 int right = numberOfNonWhitePixels(x2, image.width() - x2, image);
874 text->setHAlign(QQuickText::AlignRight);
875 QImage image = view.grabFrameBuffer();
876 int x = image.width() - qCeil(text->implicitWidth());
877 int left = numberOfNonWhitePixels(0, x, image);
878 int right = numberOfNonWhitePixels(x, image.width() - x, image);
884 void tst_qquicktextedit::vAlign()
886 //test one align each, and then test if two align fails.
888 for (int i = 0; i < standard.size(); i++)
890 for (int j=0; j < vAlignmentStrings.size(); j++)
892 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
893 QQmlComponent texteditComponent(&engine);
894 texteditComponent.setData(componentStr.toLatin1(), QUrl());
895 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
897 QVERIFY(textEditObject != 0);
898 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
902 for (int i = 0; i < richText.size(); i++)
904 for (int j=0; j < vAlignmentStrings.size(); j++)
906 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
907 QQmlComponent texteditComponent(&engine);
908 texteditComponent.setData(componentStr.toLatin1(), QUrl());
909 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
911 QVERIFY(textEditObject != 0);
912 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
916 QQmlComponent texteditComponent(&engine);
917 texteditComponent.setData(
918 "import QtQuick 2.0\n"
919 "TextEdit { width: 100; height: 100; text: \"Hello World\" }", QUrl());
920 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
922 QVERIFY(textEditObject != 0);
924 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignTop);
925 QVERIFY(textEditObject->cursorRectangle().bottom() < 50);
926 QVERIFY(textEditObject->positionToRectangle(0).bottom() < 50);
929 textEditObject->setVAlign(QQuickTextEdit::AlignBottom);
930 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignBottom);
931 QVERIFY(textEditObject->cursorRectangle().top() > 50);
932 QVERIFY(textEditObject->positionToRectangle(0).top() > 50);
934 // explicitly center aligned
935 textEditObject->setVAlign(QQuickTextEdit::AlignVCenter);
936 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignVCenter);
937 QVERIFY(textEditObject->cursorRectangle().top() < 50);
938 QVERIFY(textEditObject->cursorRectangle().bottom() > 50);
939 QVERIFY(textEditObject->positionToRectangle(0).top() < 50);
940 QVERIFY(textEditObject->positionToRectangle(0).bottom() > 50);
943 void tst_qquicktextedit::font()
945 //test size, then bold, then italic, then family
947 QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
948 QQmlComponent texteditComponent(&engine);
949 texteditComponent.setData(componentStr.toLatin1(), QUrl());
950 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
952 QVERIFY(textEditObject != 0);
953 QCOMPARE(textEditObject->font().pointSize(), 40);
954 QCOMPARE(textEditObject->font().bold(), false);
955 QCOMPARE(textEditObject->font().italic(), false);
959 QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
960 QQmlComponent texteditComponent(&engine);
961 texteditComponent.setData(componentStr.toLatin1(), QUrl());
962 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
964 QVERIFY(textEditObject != 0);
965 QCOMPARE(textEditObject->font().bold(), true);
966 QCOMPARE(textEditObject->font().italic(), false);
970 QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
971 QQmlComponent texteditComponent(&engine);
972 texteditComponent.setData(componentStr.toLatin1(), QUrl());
973 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
975 QVERIFY(textEditObject != 0);
976 QCOMPARE(textEditObject->font().italic(), true);
977 QCOMPARE(textEditObject->font().bold(), false);
981 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
982 QQmlComponent texteditComponent(&engine);
983 texteditComponent.setData(componentStr.toLatin1(), QUrl());
984 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
986 QVERIFY(textEditObject != 0);
987 QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
988 QCOMPARE(textEditObject->font().bold(), false);
989 QCOMPARE(textEditObject->font().italic(), false);
993 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
994 QQmlComponent texteditComponent(&engine);
995 texteditComponent.setData(componentStr.toLatin1(), QUrl());
996 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
998 QVERIFY(textEditObject != 0);
999 QCOMPARE(textEditObject->font().family(), QString(""));
1003 void tst_qquicktextedit::color()
1005 //test initial color
1007 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
1008 QQmlComponent texteditComponent(&engine);
1009 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1010 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1012 QQuickTextEditPrivate *textEditPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(textEditObject));
1014 QVERIFY(textEditObject);
1015 QVERIFY(textEditPrivate);
1016 QVERIFY(textEditPrivate->control);
1017 QCOMPARE(textEditPrivate->color, QColor("black"));
1020 for (int i = 0; i < colorStrings.size(); i++)
1022 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1023 QQmlComponent texteditComponent(&engine);
1024 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1025 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1026 //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
1027 QVERIFY(textEditObject != 0);
1028 QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
1032 for (int i = 0; i < colorStrings.size(); i++)
1034 QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1035 QQmlComponent texteditComponent(&engine);
1036 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1037 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1038 QVERIFY(textEditObject != 0);
1039 QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
1042 //test selected text
1043 for (int i = 0; i < colorStrings.size(); i++)
1045 QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1046 QQmlComponent texteditComponent(&engine);
1047 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1048 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1049 QVERIFY(textEditObject != 0);
1050 QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
1054 QString colorStr = "#AA001234";
1055 QColor testColor("#001234");
1056 testColor.setAlpha(170);
1058 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1059 QQmlComponent texteditComponent(&engine);
1060 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1061 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1063 QVERIFY(textEditObject != 0);
1064 QCOMPARE(textEditObject->color(), testColor);
1068 void tst_qquicktextedit::textMargin()
1070 for (qreal i=0; i<=10; i+=0.3) {
1071 QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
1072 QQmlComponent texteditComponent(&engine);
1073 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1074 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1075 QVERIFY(textEditObject != 0);
1076 QCOMPARE(textEditObject->textMargin(), i);
1080 void tst_qquicktextedit::persistentSelection()
1082 QQuickView canvas(testFileUrl("persistentSelection.qml"));
1084 canvas.requestActivateWindow();
1085 QTest::qWaitForWindowShown(&canvas);
1086 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1087 canvas.requestActivateWindow();
1089 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1091 QVERIFY(edit->hasActiveFocus());
1093 QSignalSpy spy(edit, SIGNAL(persistentSelectionChanged(bool)));
1095 QCOMPARE(edit->persistentSelection(), false);
1097 edit->setPersistentSelection(false);
1098 QCOMPARE(edit->persistentSelection(), false);
1099 QCOMPARE(spy.count(), 0);
1102 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1104 edit->setFocus(false);
1105 QCOMPARE(edit->property("selected").toString(), QString());
1107 edit->setFocus(true);
1108 QCOMPARE(edit->property("selected").toString(), QString());
1110 edit->setPersistentSelection(true);
1111 QCOMPARE(edit->persistentSelection(), true);
1112 QCOMPARE(spy.count(), 1);
1115 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1117 edit->setFocus(false);
1118 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1120 edit->setFocus(true);
1121 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1125 void tst_qquicktextedit::focusOnPress()
1127 QString componentStr =
1128 "import QtQuick 2.0\n"
1130 "property bool selectOnFocus: false\n"
1131 "width: 100; height: 50\n"
1132 "activeFocusOnPress: true\n"
1133 "text: \"Hello World\"\n"
1134 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
1136 QQmlComponent texteditComponent(&engine);
1137 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1138 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1139 QVERIFY(textEditObject != 0);
1140 QCOMPARE(textEditObject->focusOnPress(), true);
1141 QCOMPARE(textEditObject->hasFocus(), false);
1143 QSignalSpy focusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1144 QSignalSpy activeFocusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1145 QSignalSpy activeFocusOnPressSpy(textEditObject, SIGNAL(activeFocusOnPressChanged(bool)));
1147 textEditObject->setFocusOnPress(true);
1148 QCOMPARE(textEditObject->focusOnPress(), true);
1149 QCOMPARE(activeFocusOnPressSpy.count(), 0);
1151 QQuickCanvas canvas;
1152 canvas.resize(100, 50);
1153 textEditObject->setParentItem(canvas.rootItem());
1155 canvas.requestActivateWindow();
1156 QTest::qWaitForWindowShown(&canvas);
1157 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
1159 QCOMPARE(textEditObject->hasFocus(), false);
1160 QCOMPARE(textEditObject->hasActiveFocus(), false);
1162 QPoint centerPoint(canvas.width()/2, canvas.height()/2);
1163 Qt::KeyboardModifiers noModifiers = 0;
1164 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1165 QGuiApplication::processEvents();
1166 QCOMPARE(textEditObject->hasFocus(), true);
1167 QCOMPARE(textEditObject->hasActiveFocus(), true);
1168 QCOMPARE(focusSpy.count(), 1);
1169 QCOMPARE(activeFocusSpy.count(), 1);
1170 QCOMPARE(textEditObject->selectedText(), QString());
1171 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1173 textEditObject->setFocusOnPress(false);
1174 QCOMPARE(textEditObject->focusOnPress(), false);
1175 QCOMPARE(activeFocusOnPressSpy.count(), 1);
1177 textEditObject->setFocus(false);
1178 QCOMPARE(textEditObject->hasFocus(), false);
1179 QCOMPARE(textEditObject->hasActiveFocus(), false);
1180 QCOMPARE(focusSpy.count(), 2);
1181 QCOMPARE(activeFocusSpy.count(), 2);
1183 // Wait for double click timeout to expire before clicking again.
1185 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1186 QGuiApplication::processEvents();
1187 QCOMPARE(textEditObject->hasFocus(), false);
1188 QCOMPARE(textEditObject->hasActiveFocus(), false);
1189 QCOMPARE(focusSpy.count(), 2);
1190 QCOMPARE(activeFocusSpy.count(), 2);
1191 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1193 textEditObject->setFocusOnPress(true);
1194 QCOMPARE(textEditObject->focusOnPress(), true);
1195 QCOMPARE(activeFocusOnPressSpy.count(), 2);
1197 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
1198 textEditObject->setProperty("selectOnFocus", true);
1201 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1202 QGuiApplication::processEvents();
1203 QCOMPARE(textEditObject->hasFocus(), true);
1204 QCOMPARE(textEditObject->hasActiveFocus(), true);
1205 QCOMPARE(focusSpy.count(), 3);
1206 QCOMPARE(activeFocusSpy.count(), 3);
1207 QCOMPARE(textEditObject->selectedText(), textEditObject->text());
1208 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1211 void tst_qquicktextedit::selection()
1213 QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
1214 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1215 QQmlComponent texteditComponent(&engine);
1216 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1217 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1218 QVERIFY(textEditObject != 0);
1221 //Test selection follows cursor
1222 for (int i=0; i<= testStr.size(); i++) {
1223 textEditObject->setCursorPosition(i);
1224 QCOMPARE(textEditObject->cursorPosition(), i);
1225 QCOMPARE(textEditObject->selectionStart(), i);
1226 QCOMPARE(textEditObject->selectionEnd(), i);
1227 QVERIFY(textEditObject->selectedText().isNull());
1230 textEditObject->setCursorPosition(0);
1231 QVERIFY(textEditObject->cursorPosition() == 0);
1232 QVERIFY(textEditObject->selectionStart() == 0);
1233 QVERIFY(textEditObject->selectionEnd() == 0);
1234 QVERIFY(textEditObject->selectedText().isNull());
1236 // Verify invalid positions are ignored.
1237 textEditObject->setCursorPosition(-1);
1238 QVERIFY(textEditObject->cursorPosition() == 0);
1239 QVERIFY(textEditObject->selectionStart() == 0);
1240 QVERIFY(textEditObject->selectionEnd() == 0);
1241 QVERIFY(textEditObject->selectedText().isNull());
1243 textEditObject->setCursorPosition(textEditObject->text().count()+1);
1244 QVERIFY(textEditObject->cursorPosition() == 0);
1245 QVERIFY(textEditObject->selectionStart() == 0);
1246 QVERIFY(textEditObject->selectionEnd() == 0);
1247 QVERIFY(textEditObject->selectedText().isNull());
1250 for (int i=0; i<= testStr.size(); i++) {
1251 textEditObject->select(0,i);
1252 QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
1254 for (int i=0; i<= testStr.size(); i++) {
1255 textEditObject->select(i,testStr.size());
1256 QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
1259 textEditObject->setCursorPosition(0);
1260 QVERIFY(textEditObject->cursorPosition() == 0);
1261 QVERIFY(textEditObject->selectionStart() == 0);
1262 QVERIFY(textEditObject->selectionEnd() == 0);
1263 QVERIFY(textEditObject->selectedText().isNull());
1265 //Test Error Ignoring behaviour
1266 textEditObject->setCursorPosition(0);
1267 QVERIFY(textEditObject->selectedText().isNull());
1268 textEditObject->select(-10,0);
1269 QVERIFY(textEditObject->selectedText().isNull());
1270 textEditObject->select(100,101);
1271 QVERIFY(textEditObject->selectedText().isNull());
1272 textEditObject->select(0,-10);
1273 QVERIFY(textEditObject->selectedText().isNull());
1274 textEditObject->select(0,100);
1275 QVERIFY(textEditObject->selectedText().isNull());
1276 textEditObject->select(0,10);
1277 QVERIFY(textEditObject->selectedText().size() == 10);
1278 textEditObject->select(-10,0);
1279 QVERIFY(textEditObject->selectedText().size() == 10);
1280 textEditObject->select(100,101);
1281 QVERIFY(textEditObject->selectedText().size() == 10);
1282 textEditObject->select(0,-10);
1283 QVERIFY(textEditObject->selectedText().size() == 10);
1284 textEditObject->select(0,100);
1285 QVERIFY(textEditObject->selectedText().size() == 10);
1287 textEditObject->deselect();
1288 QVERIFY(textEditObject->selectedText().isNull());
1289 textEditObject->select(0,10);
1290 QVERIFY(textEditObject->selectedText().size() == 10);
1291 textEditObject->deselect();
1292 QVERIFY(textEditObject->selectedText().isNull());
1295 void tst_qquicktextedit::isRightToLeft_data()
1297 QTest::addColumn<QString>("text");
1298 QTest::addColumn<bool>("emptyString");
1299 QTest::addColumn<bool>("firstCharacter");
1300 QTest::addColumn<bool>("lastCharacter");
1301 QTest::addColumn<bool>("middleCharacter");
1302 QTest::addColumn<bool>("startString");
1303 QTest::addColumn<bool>("midString");
1304 QTest::addColumn<bool>("endString");
1306 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1307 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
1308 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
1309 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
1310 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
1311 QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
1312 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
1315 void tst_qquicktextedit::isRightToLeft()
1317 QFETCH(QString, text);
1318 QFETCH(bool, emptyString);
1319 QFETCH(bool, firstCharacter);
1320 QFETCH(bool, lastCharacter);
1321 QFETCH(bool, middleCharacter);
1322 QFETCH(bool, startString);
1323 QFETCH(bool, midString);
1324 QFETCH(bool, endString);
1326 QQuickTextEdit textEdit;
1327 textEdit.setText(text);
1329 // first test that the right string is delivered to the QString::isRightToLeft()
1330 QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
1331 QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
1332 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
1333 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
1334 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
1335 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
1337 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1338 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
1340 // then test that the feature actually works
1341 QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
1342 QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
1343 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
1344 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
1345 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
1346 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
1348 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1349 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
1352 void tst_qquicktextedit::keySelection()
1354 QQuickView canvas(testFileUrl("navigation.qml"));
1356 canvas.requestActivateWindow();
1357 QTest::qWaitForWindowShown(&canvas);
1358 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1359 canvas.requestActivateWindow();
1361 QVERIFY(canvas.rootObject() != 0);
1363 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1365 QVERIFY(input != 0);
1366 QTRY_VERIFY(input->hasActiveFocus() == true);
1368 QSignalSpy spy(input, SIGNAL(selectionChanged()));
1370 simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
1371 QVERIFY(input->hasActiveFocus() == true);
1372 QCOMPARE(input->selectedText(), QString("a"));
1373 QCOMPARE(spy.count(), 1);
1374 simulateKey(&canvas, Qt::Key_Right);
1375 QVERIFY(input->hasActiveFocus() == true);
1376 QCOMPARE(input->selectedText(), QString());
1377 QCOMPARE(spy.count(), 2);
1378 simulateKey(&canvas, Qt::Key_Right);
1379 QVERIFY(input->hasActiveFocus() == false);
1380 QCOMPARE(input->selectedText(), QString());
1381 QCOMPARE(spy.count(), 2);
1383 simulateKey(&canvas, Qt::Key_Left);
1384 QVERIFY(input->hasActiveFocus() == true);
1385 QCOMPARE(spy.count(), 2);
1386 simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1387 QVERIFY(input->hasActiveFocus() == true);
1388 QCOMPARE(input->selectedText(), QString("a"));
1389 QCOMPARE(spy.count(), 3);
1390 simulateKey(&canvas, Qt::Key_Left);
1391 QVERIFY(input->hasActiveFocus() == true);
1392 QCOMPARE(input->selectedText(), QString());
1393 QCOMPARE(spy.count(), 4);
1394 simulateKey(&canvas, Qt::Key_Left);
1395 QVERIFY(input->hasActiveFocus() == false);
1396 QCOMPARE(input->selectedText(), QString());
1397 QCOMPARE(spy.count(), 4);
1400 void tst_qquicktextedit::moveCursorSelection_data()
1402 QTest::addColumn<QString>("testStr");
1403 QTest::addColumn<int>("cursorPosition");
1404 QTest::addColumn<int>("movePosition");
1405 QTest::addColumn<QQuickTextEdit::SelectionMode>("mode");
1406 QTest::addColumn<int>("selectionStart");
1407 QTest::addColumn<int>("selectionEnd");
1408 QTest::addColumn<bool>("reversible");
1410 QTest::newRow("(t)he|characters")
1411 << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true;
1412 QTest::newRow("do(g)|characters")
1413 << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true;
1414 QTest::newRow("jum(p)ed|characters")
1415 << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true;
1416 QTest::newRow("jumped( )over|characters")
1417 << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true;
1418 QTest::newRow("(the )|characters")
1419 << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true;
1420 QTest::newRow("( dog)|characters")
1421 << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true;
1422 QTest::newRow("( jumped )|characters")
1423 << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true;
1424 QTest::newRow("th(e qu)ick|characters")
1425 << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true;
1426 QTest::newRow("la(zy d)og|characters")
1427 << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true;
1428 QTest::newRow("jum(ped ov)er|characters")
1429 << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true;
1430 QTest::newRow("()the|characters")
1431 << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true;
1432 QTest::newRow("dog()|characters")
1433 << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true;
1434 QTest::newRow("jum()ped|characters")
1435 << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true;
1437 QTest::newRow("<(t)he>|words")
1438 << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true;
1439 QTest::newRow("<do(g)>|words")
1440 << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true;
1441 QTest::newRow("<jum(p)ed>|words")
1442 << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true;
1443 QTest::newRow("<jumped( )>over|words")
1444 << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false;
1445 QTest::newRow("jumped<( )over>|words,reversed")
1446 << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false;
1447 QTest::newRow("<(the )>quick|words")
1448 << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false;
1449 QTest::newRow("<(the )quick>|words,reversed")
1450 << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false;
1451 QTest::newRow("<lazy( dog)>|words")
1452 << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false;
1453 QTest::newRow("lazy<( dog)>|words,reversed")
1454 << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false;
1455 QTest::newRow("<fox( jumped )>over|words")
1456 << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false;
1457 QTest::newRow("fox<( jumped )over>|words,reversed")
1458 << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false;
1459 QTest::newRow("<th(e qu)ick>|words")
1460 << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true;
1461 QTest::newRow("<la(zy d)og|words>")
1462 << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true;
1463 QTest::newRow("<jum(ped ov)er>|words")
1464 << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true;
1465 QTest::newRow("<()>the|words")
1466 << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1467 QTest::newRow("dog<()>|words")
1468 << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true;
1469 QTest::newRow("jum<()>ped|words")
1470 << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true;
1472 QTest::newRow("Hello<(,)> |words")
1473 << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true;
1474 QTest::newRow("Hello<(, )>world|words")
1475 << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1476 QTest::newRow("Hello<(, )world>|words,reversed")
1477 << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1478 QTest::newRow("<Hel(lo, )>world|words")
1479 << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1480 QTest::newRow("<Hel(lo, )world>|words,reversed")
1481 << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false;
1482 QTest::newRow("<Hel(lo)>,|words")
1483 << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true;
1484 QTest::newRow("Hello<()>,|words")
1485 << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true;
1486 QTest::newRow("Hello,<()>|words")
1487 << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true;
1488 QTest::newRow("Hello<,( )>world|words")
1489 << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1490 QTest::newRow("Hello,<( )world>|words,reversed")
1491 << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1492 QTest::newRow("Hello<,( world)>|words")
1493 << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1494 QTest::newRow("Hello,<( world)>|words,reversed")
1495 << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1496 QTest::newRow("Hello<,( world!)>|words")
1497 << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false;
1498 QTest::newRow("Hello,<( world!)>|words,reversed")
1499 << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1500 QTest::newRow("Hello<(, world!)>|words")
1501 << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true;
1502 QTest::newRow("world<(!)>|words")
1503 << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true;
1504 QTest::newRow("world!<()>)|words")
1505 << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true;
1506 QTest::newRow("world<()>!)|words")
1507 << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true;
1509 QTest::newRow("<(,)>olleH |words")
1510 << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true;
1511 QTest::newRow("<dlrow( ,)>olleH|words")
1512 << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1513 QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1514 << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1515 QTest::newRow("<dlrow( ,ol)leH>|words")
1516 << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false;
1517 QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1518 << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1519 QTest::newRow(",<(ol)leH>,|words")
1520 << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true;
1521 QTest::newRow(",<()>olleH|words")
1522 << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true;
1523 QTest::newRow("<()>,olleH|words")
1524 << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true;
1525 QTest::newRow("<dlrow( )>,olleH|words")
1526 << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1527 QTest::newRow("dlrow<( ),>olleH|words,reversed")
1528 << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1529 QTest::newRow("<(dlrow )>,olleH|words")
1530 << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1531 QTest::newRow("<(dlrow ),>olleH|words,reversed")
1532 << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1533 QTest::newRow("<(!dlrow )>,olleH|words")
1534 << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1535 QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1536 << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false;
1537 QTest::newRow("(!dlrow ,)olleH|words")
1538 << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true;
1539 QTest::newRow("<(!)>dlrow|words")
1540 << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true;
1541 QTest::newRow("<()>!dlrow|words")
1542 << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1543 QTest::newRow("!<()>dlrow|words")
1544 << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true;
1547 void tst_qquicktextedit::moveCursorSelection()
1549 QFETCH(QString, testStr);
1550 QFETCH(int, cursorPosition);
1551 QFETCH(int, movePosition);
1552 QFETCH(QQuickTextEdit::SelectionMode, mode);
1553 QFETCH(int, selectionStart);
1554 QFETCH(int, selectionEnd);
1555 QFETCH(bool, reversible);
1557 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1558 QQmlComponent textinputComponent(&engine);
1559 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1560 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(textinputComponent.create());
1561 QVERIFY(texteditObject != 0);
1563 texteditObject->setCursorPosition(cursorPosition);
1564 texteditObject->moveCursorSelection(movePosition, mode);
1566 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1567 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1568 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1571 texteditObject->setCursorPosition(movePosition);
1572 texteditObject->moveCursorSelection(cursorPosition, mode);
1574 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1575 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1576 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1580 void tst_qquicktextedit::moveCursorSelectionSequence_data()
1582 QTest::addColumn<QString>("testStr");
1583 QTest::addColumn<int>("cursorPosition");
1584 QTest::addColumn<int>("movePosition1");
1585 QTest::addColumn<int>("movePosition2");
1586 QTest::addColumn<int>("selection1Start");
1587 QTest::addColumn<int>("selection1End");
1588 QTest::addColumn<int>("selection2Start");
1589 QTest::addColumn<int>("selection2End");
1591 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1596 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1601 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1606 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1611 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1616 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1621 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1626 QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1631 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1636 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1641 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1646 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1651 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1656 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1661 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1666 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1671 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1676 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1682 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1687 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1693 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1698 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1705 void tst_qquicktextedit::moveCursorSelectionSequence()
1707 QFETCH(QString, testStr);
1708 QFETCH(int, cursorPosition);
1709 QFETCH(int, movePosition1);
1710 QFETCH(int, movePosition2);
1711 QFETCH(int, selection1Start);
1712 QFETCH(int, selection1End);
1713 QFETCH(int, selection2Start);
1714 QFETCH(int, selection2End);
1716 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1717 QQmlComponent texteditComponent(&engine);
1718 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1719 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1720 QVERIFY(texteditObject != 0);
1722 texteditObject->setCursorPosition(cursorPosition);
1724 texteditObject->moveCursorSelection(movePosition1, QQuickTextEdit::SelectWords);
1725 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1726 QCOMPARE(texteditObject->selectionStart(), selection1Start);
1727 QCOMPARE(texteditObject->selectionEnd(), selection1End);
1729 texteditObject->moveCursorSelection(movePosition2, QQuickTextEdit::SelectWords);
1730 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1731 QCOMPARE(texteditObject->selectionStart(), selection2Start);
1732 QCOMPARE(texteditObject->selectionEnd(), selection2End);
1736 void tst_qquicktextedit::mouseSelection_data()
1738 QTest::addColumn<QString>("qmlfile");
1739 QTest::addColumn<int>("from");
1740 QTest::addColumn<int>("to");
1741 QTest::addColumn<QString>("selectedText");
1742 QTest::addColumn<bool>("focus");
1743 QTest::addColumn<bool>("focusOnPress");
1744 QTest::addColumn<bool>("doubleClick");
1747 QTest::newRow("on") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << true << true << false;
1748 QTest::newRow("off") << testFile("mouseselection_false.qml") << 4 << 9 << QString() << true << true << false;
1749 QTest::newRow("default") << testFile("mouseselection_default.qml") << 4 << 9 << QString() << true << true << false;
1750 QTest::newRow("off word selection") << testFile("mouseselection_false_words.qml") << 4 << 9 << QString() << true << true << false;
1751 QTest::newRow("on word selection (4,9)") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << true << true << false;
1753 QTest::newRow("on unfocused") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << false << false;
1754 QTest::newRow("on word selection (4,9) unfocused") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << false << false;
1756 QTest::newRow("on focus on press") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << true << false;
1757 QTest::newRow("on word selection (4,9) focus on press") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << true << false;
1759 QTest::newRow("on word selection (2,13)") << testFile("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1760 QTest::newRow("on word selection (2,30)") << testFile("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1761 QTest::newRow("on word selection (9,13)") << testFile("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1762 QTest::newRow("on word selection (9,30)") << testFile("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1763 QTest::newRow("on word selection (13,2)") << testFile("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1764 QTest::newRow("on word selection (20,2)") << testFile("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1765 QTest::newRow("on word selection (12,9)") << testFile("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1766 QTest::newRow("on word selection (30,9)") << testFile("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << false;
1768 QTest::newRow("off double click (4,9)") << testFile("mouseselection_true.qml") << 4 << 9 << "0123456789" << true << true << true;
1769 QTest::newRow("off double click (2,13)") << testFile("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1770 QTest::newRow("off double click (2,30)") << testFile("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1771 QTest::newRow("off double click (9,13)") << testFile("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1772 QTest::newRow("off double click (9,30)") << testFile("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1773 QTest::newRow("off double click (13,2)") << testFile("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1774 QTest::newRow("off double click (20,2)") << testFile("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1775 QTest::newRow("off double click (12,9)") << testFile("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1776 QTest::newRow("off double click (30,9)") << testFile("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << true;
1779 void tst_qquicktextedit::mouseSelection()
1781 QFETCH(QString, qmlfile);
1784 QFETCH(QString, selectedText);
1785 QFETCH(bool, focus);
1786 QFETCH(bool, focusOnPress);
1787 QFETCH(bool, doubleClick);
1789 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1792 canvas.requestActivateWindow();
1793 QTest::qWaitForWindowShown(&canvas);
1794 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1796 QVERIFY(canvas.rootObject() != 0);
1797 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1798 QVERIFY(textEditObject != 0);
1800 textEditObject->setFocus(focus);
1801 textEditObject->setFocusOnPress(focusOnPress);
1803 // press-and-drag-and-release from x1 to x2
1804 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1805 QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1807 QTest::mouseClick(&canvas, Qt::LeftButton, 0, p1);
1808 QTest::mousePress(&canvas, Qt::LeftButton, 0, p1);
1809 QTest::mouseMove(&canvas, p2);
1810 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2);
1812 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1814 // Clicking and shift to clicking between the same points should select the same text.
1815 textEditObject->setCursorPosition(0);
1817 QTest::mouseDClick(&canvas, Qt::LeftButton, 0, p1);
1819 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1820 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1822 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1825 void tst_qquicktextedit::dragMouseSelection()
1827 QString qmlfile = testFile("mouseselection_true.qml");
1829 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1832 canvas.requestActivateWindow();
1833 QTest::qWaitForWindowShown(&canvas);
1834 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1836 QVERIFY(canvas.rootObject() != 0);
1837 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1838 QVERIFY(textEditObject != 0);
1840 // press-and-drag-and-release from x1 to x2
1843 int y = textEditObject->height()/2;
1844 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1845 QTest::mouseMove(&canvas, QPoint(x2, y));
1846 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1849 QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1851 // press and drag the current selection.
1854 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1855 QTest::mouseMove(&canvas, QPoint(x2, y));
1856 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1859 QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1861 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1864 void tst_qquicktextedit::mouseSelectionMode_data()
1866 QTest::addColumn<QString>("qmlfile");
1867 QTest::addColumn<bool>("selectWords");
1870 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1871 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1872 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1875 void tst_qquicktextedit::mouseSelectionMode()
1877 QFETCH(QString, qmlfile);
1878 QFETCH(bool, selectWords);
1880 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1882 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1885 canvas.requestActivateWindow();
1886 QTest::qWaitForWindowShown(&canvas);
1887 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1889 QVERIFY(canvas.rootObject() != 0);
1890 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1891 QVERIFY(textEditObject != 0);
1893 // press-and-drag-and-release from x1 to x2
1896 int y = textEditObject->height()/2;
1897 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1898 QTest::mouseMove(&canvas, QPoint(x2, y));
1899 //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
1900 // QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1901 // QGuiApplication::sendEvent(&canvas, &mv);
1902 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1903 QString str = textEditObject->selectedText();
1905 QTRY_COMPARE(textEditObject->selectedText(), text);
1907 QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1908 QVERIFY(str != text);
1912 void tst_qquicktextedit::inputMethodHints()
1914 QQuickView canvas(testFileUrl("inputmethodhints.qml"));
1916 canvas.requestActivateWindow();
1918 QVERIFY(canvas.rootObject() != 0);
1919 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1920 QVERIFY(textEditObject != 0);
1921 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1922 QSignalSpy inputMethodHintSpy(textEditObject, SIGNAL(inputMethodHintsChanged()));
1923 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1924 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1925 QCOMPARE(inputMethodHintSpy.count(), 1);
1926 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1927 QCOMPARE(inputMethodHintSpy.count(), 1);
1929 QQuickTextEdit plainTextEdit;
1930 QCOMPARE(plainTextEdit.inputMethodHints(), Qt::ImhNone);
1933 void tst_qquicktextedit::positionAt()
1935 QQuickView canvas(testFileUrl("positionAt.qml"));
1936 QVERIFY(canvas.rootObject() != 0);
1938 canvas.requestActivateWindow();
1939 canvas.requestActivateWindow();
1940 QTest::qWaitForWindowShown(&canvas);
1942 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1943 QVERIFY(texteditObject != 0);
1945 QTextLayout layout(texteditObject->text());
1946 layout.setFont(texteditObject->font());
1948 if (!qmlDisableDistanceField()) {
1950 option.setUseDesignMetrics(true);
1951 layout.setTextOption(option);
1954 layout.beginLayout();
1955 QTextLine line = layout.createLine();
1958 const int y0 = line.height() / 2;
1959 const int y1 = line.height() * 3 / 2;
1961 int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
1963 int widthBegin = floor(line.cursorToX(pos - 1));
1964 int widthEnd = ceil(line.cursorToX(pos + 1));
1966 QVERIFY(widthBegin <= texteditObject->width() / 2);
1967 QVERIFY(widthEnd >= texteditObject->width() / 2);
1969 const qreal x0 = texteditObject->positionToRectangle(pos).x();
1970 const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
1972 QString preeditText = texteditObject->text().mid(0, pos);
1973 texteditObject->setText(texteditObject->text().mid(pos));
1974 texteditObject->setCursorPosition(0);
1976 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1977 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent);
1979 // Check all points within the preedit text return the same position.
1980 QCOMPARE(texteditObject->positionAt(0, y0), 0);
1981 QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
1982 QCOMPARE(texteditObject->positionAt(x0, y0), 0);
1984 // Verify positioning returns to normal after the preedit text.
1985 QCOMPARE(texteditObject->positionAt(x1, y0), 1);
1986 QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
1988 QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
1991 void tst_qquicktextedit::linkActivated()
1993 QQuickView canvas(testFileUrl("linkActivated.qml"));
1994 QVERIFY(canvas.rootObject() != 0);
1996 canvas.requestActivateWindow();
1997 QTest::qWaitForWindowShown(&canvas);
1999 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
2000 QVERIFY(texteditObject != 0);
2002 QSignalSpy spy(texteditObject, SIGNAL(linkActivated(QString)));
2004 const QString link("http://example.com/");
2006 const QPointF linkPos = texteditObject->positionToRectangle(7).center();
2007 const QPointF textPos = texteditObject->positionToRectangle(2).center();
2009 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
2010 QTRY_COMPARE(spy.count(), 1);
2011 QCOMPARE(spy.last()[0].toString(), link);
2013 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
2015 QCOMPARE(spy.count(), 1);
2017 texteditObject->setReadOnly(true);
2019 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
2020 QTRY_COMPARE(spy.count(), 2);
2021 QCOMPARE(spy.last()[0].toString(), link);
2023 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
2025 QCOMPARE(spy.count(), 2);
2028 void tst_qquicktextedit::cursorDelegate_data()
2030 QTest::addColumn<QUrl>("source");
2031 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2032 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2033 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2036 void tst_qquicktextedit::cursorDelegate()
2038 QFETCH(QUrl, source);
2039 QQuickView view(source);
2041 view.requestActivateWindow();
2042 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
2043 QVERIFY(textEditObject != 0);
2044 // Delegate creation is deferred until focus in or cursor visiblity is forced.
2045 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2046 QVERIFY(!textEditObject->isCursorVisible());
2047 //Test Delegate gets created
2048 textEditObject->setFocus(true);
2049 QVERIFY(textEditObject->isCursorVisible());
2050 QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>("cursorInstance");
2051 QVERIFY(delegateObject);
2052 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2053 //Test Delegate gets moved
2054 for (int i=0; i<= textEditObject->text().length(); i++) {
2055 textEditObject->setCursorPosition(i);
2056 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2057 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2060 // Test delegate gets moved on mouse press.
2061 textEditObject->setSelectByMouse(true);
2062 textEditObject->setCursorPosition(0);
2063 const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
2064 QTest::qWait(400); //ensure this isn't treated as a double-click
2065 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2067 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2068 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2069 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2071 // Test delegate gets moved on mouse drag
2072 textEditObject->setCursorPosition(0);
2073 const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
2074 QTest::qWait(400); //ensure this isn't treated as a double-click
2075 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2076 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2077 QGuiApplication::sendEvent(&view, &mv);
2078 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2080 QTRY_COMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2081 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2083 textEditObject->setReadOnly(true);
2084 textEditObject->setCursorPosition(0);
2085 QTest::qWait(400); //ensure this isn't treated as a double-click
2086 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
2088 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2089 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2090 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2092 textEditObject->setCursorPosition(0);
2093 QTest::qWait(400); //ensure this isn't treated as a double-click
2094 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
2096 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2097 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2098 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2100 textEditObject->setCursorPosition(0);
2101 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2102 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2104 textEditObject->setReadOnly(false);
2106 // Delegate moved when text is entered
2107 textEditObject->setText(QString());
2108 for (int i = 0; i < 20; ++i) {
2109 QTest::keyClick(&view, Qt::Key_A);
2110 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2111 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2114 // Delegate moved when text is entered by im.
2115 textEditObject->setText(QString());
2116 for (int i = 0; i < 20; ++i) {
2117 QInputMethodEvent event;
2118 event.setCommitString("a");
2119 QGuiApplication::sendEvent(textEditObject, &event);
2120 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2121 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2123 // Delegate moved when text is removed by im.
2124 for (int i = 19; i > 1; --i) {
2125 QInputMethodEvent event;
2126 event.setCommitString(QString(), -1, 1);
2127 QGuiApplication::sendEvent(textEditObject, &event);
2128 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2129 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2131 { // Delegate moved the text is changed in place by im.
2132 QInputMethodEvent event;
2133 event.setCommitString("i", -1, 1);
2134 QGuiApplication::sendEvent(textEditObject, &event);
2135 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2136 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2139 //Test Delegate gets deleted
2140 textEditObject->setCursorDelegate(0);
2141 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2144 void tst_qquicktextedit::cursorVisible()
2146 QQuickTextEdit edit;
2147 edit.componentComplete();
2148 QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
2150 QQuickView view(testFileUrl("cursorVisible.qml"));
2152 view.requestActivateWindow();
2153 QTest::qWaitForWindowShown(&view);
2154 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2156 QCOMPARE(edit.isCursorVisible(), false);
2158 edit.setCursorVisible(true);
2159 QCOMPARE(edit.isCursorVisible(), true);
2160 QCOMPARE(spy.count(), 1);
2162 edit.setCursorVisible(false);
2163 QCOMPARE(edit.isCursorVisible(), false);
2164 QCOMPARE(spy.count(), 2);
2166 edit.setFocus(true);
2167 QCOMPARE(edit.isCursorVisible(), false);
2168 QCOMPARE(spy.count(), 2);
2170 edit.setParentItem(view.rootObject());
2171 QCOMPARE(edit.isCursorVisible(), true);
2172 QCOMPARE(spy.count(), 3);
2174 edit.setFocus(false);
2175 QCOMPARE(edit.isCursorVisible(), false);
2176 QCOMPARE(spy.count(), 4);
2178 edit.setFocus(true);
2179 QCOMPARE(edit.isCursorVisible(), true);
2180 QCOMPARE(spy.count(), 5);
2182 QWindow alternateView;
2183 alternateView.show();
2184 alternateView.requestActivateWindow();
2185 QTest::qWaitForWindowShown(&alternateView);
2187 QCOMPARE(edit.isCursorVisible(), false);
2188 QCOMPARE(spy.count(), 6);
2190 view.requestActivateWindow();
2191 QTest::qWaitForWindowShown(&view);
2192 QCOMPARE(edit.isCursorVisible(), true);
2193 QCOMPARE(spy.count(), 7);
2195 { // Cursor attribute with 0 length hides cursor.
2196 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2197 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2198 QCoreApplication::sendEvent(&edit, &ev);
2200 QCOMPARE(edit.isCursorVisible(), false);
2201 QCOMPARE(spy.count(), 8);
2203 { // Cursor attribute with non zero length shows cursor.
2204 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2205 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2206 QCoreApplication::sendEvent(&edit, &ev);
2208 QCOMPARE(edit.isCursorVisible(), true);
2209 QCOMPARE(spy.count(), 9);
2212 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2213 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2214 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2215 QCoreApplication::sendEvent(&edit, &ev);
2217 QCOMPARE(edit.isCursorVisible(), false);
2218 QCOMPARE(spy.count(), 10);
2220 edit.setText("something");
2221 QCOMPARE(edit.isCursorVisible(), true);
2222 QCOMPARE(spy.count(), 11);
2224 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2225 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2226 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2227 QCoreApplication::sendEvent(&edit, &ev);
2229 QCOMPARE(edit.isCursorVisible(), false);
2230 QCOMPARE(spy.count(), 12);
2232 edit.setCursorPosition(5);
2233 QCOMPARE(edit.isCursorVisible(), true);
2234 QCOMPARE(spy.count(), 13);
2237 void tst_qquicktextedit::delegateLoading_data()
2239 QTest::addColumn<QString>("qmlfile");
2240 QTest::addColumn<QString>("error");
2243 QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
2244 QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
2245 QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
2248 void tst_qquicktextedit::delegateLoading()
2250 QFETCH(QString, qmlfile);
2251 QFETCH(QString, error);
2253 TestHTTPServer server(42332);
2254 server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect);
2255 server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay);
2256 server.serveDirectory(testFile("http"));
2258 QQuickView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
2260 view.requestActivateWindow();
2262 if (!error.isEmpty()) {
2263 QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
2264 QTRY_VERIFY(view.status()==QQuickView::Error);
2265 QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
2267 QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
2268 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
2269 // view.rootObject()->dumpObjectTree();
2270 QVERIFY(textEditObject != 0);
2271 textEditObject->setFocus(true);
2272 QQuickItem *delegate;
2273 delegate = view.rootObject()->findChild<QQuickItem*>("delegateOkay");
2275 delegate = view.rootObject()->findChild<QQuickItem*>("delegateSlow");
2282 //A test should be added here with a component which is ready but component.create() returns null
2283 //Not sure how to accomplish this with QQuickTextEdits cursor delegate
2284 //###This was only needed for code coverage, and could be a case of overzealous defensive programming
2285 //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB");
2286 //QVERIFY(!delegate);
2290 TextEdit element should only handle left/right keys until the cursor reaches
2291 the extent of the text, then they should ignore the keys.
2293 void tst_qquicktextedit::navigation()
2295 QQuickView canvas(testFileUrl("navigation.qml"));
2297 canvas.requestActivateWindow();
2299 QVERIFY(canvas.rootObject() != 0);
2301 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2303 QVERIFY(input != 0);
2304 QTRY_VERIFY(input->hasActiveFocus() == true);
2305 simulateKey(&canvas, Qt::Key_Left);
2306 QVERIFY(input->hasActiveFocus() == false);
2307 simulateKey(&canvas, Qt::Key_Right);
2308 QVERIFY(input->hasActiveFocus() == true);
2309 simulateKey(&canvas, Qt::Key_Right);
2310 QVERIFY(input->hasActiveFocus() == true);
2311 simulateKey(&canvas, Qt::Key_Right);
2312 QVERIFY(input->hasActiveFocus() == false);
2313 simulateKey(&canvas, Qt::Key_Left);
2314 QVERIFY(input->hasActiveFocus() == true);
2316 // Test left and right navigation works if the TextEdit is empty (QTBUG-25447).
2317 input->setText(QString());
2318 QCOMPARE(input->cursorPosition(), 0);
2319 simulateKey(&canvas, Qt::Key_Right);
2320 QCOMPARE(input->hasActiveFocus(), false);
2321 simulateKey(&canvas, Qt::Key_Left);
2322 QCOMPARE(input->hasActiveFocus(), true);
2323 simulateKey(&canvas, Qt::Key_Left);
2324 QCOMPARE(input->hasActiveFocus(), false);
2327 void tst_qquicktextedit::copyAndPaste() {
2328 #ifndef QT_NO_CLIPBOARD
2332 PasteboardRef pasteboard;
2333 OSStatus status = PasteboardCreate(0, &pasteboard);
2334 if (status == noErr)
2335 CFRelease(pasteboard);
2337 QSKIP("This machine doesn't support the clipboard");
2341 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2342 QQmlComponent textEditComponent(&engine);
2343 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2344 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2345 QVERIFY(textEdit != 0);
2348 QCOMPARE(textEdit->text().length(), 12);
2349 textEdit->select(0, textEdit->text().length());;
2351 QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
2352 QCOMPARE(textEdit->selectedText().length(), 12);
2353 textEdit->setCursorPosition(0);
2354 QVERIFY(textEdit->canPaste());
2356 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2357 QCOMPARE(textEdit->text().length(), 24);
2360 QVERIFY(textEdit->canPaste());
2361 textEdit->setReadOnly(true);
2362 QVERIFY(!textEdit->canPaste());
2363 textEdit->setReadOnly(false);
2364 QVERIFY(textEdit->canPaste());
2367 // test that document and internal text attribute are in sync
2368 QQuickItemPrivate* pri = QQuickItemPrivate::get(textEdit);
2369 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri);
2370 QCOMPARE(textEdit->text(), editPrivate->text);
2373 textEdit->setCursorPosition(0);
2374 textEdit->selectWord();
2375 QCOMPARE(textEdit->selectedText(), QString("Hello"));
2377 // select all and cut
2378 textEdit->selectAll();
2380 QCOMPARE(textEdit->text().length(), 0);
2382 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2383 QCOMPARE(textEdit->text().length(), 24);
2387 void tst_qquicktextedit::canPaste() {
2388 #ifndef QT_NO_CLIPBOARD
2390 QGuiApplication::clipboard()->setText("Some text");
2392 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2393 QQmlComponent textEditComponent(&engine);
2394 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2395 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2396 QVERIFY(textEdit != 0);
2398 // check initial value - QTBUG-17765
2399 QQuickTextControl tc(0);
2400 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2405 void tst_qquicktextedit::canPasteEmpty() {
2406 #ifndef QT_NO_CLIPBOARD
2408 QGuiApplication::clipboard()->clear();
2410 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2411 QQmlComponent textEditComponent(&engine);
2412 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2413 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2414 QVERIFY(textEdit != 0);
2416 // check initial value - QTBUG-17765
2417 QQuickTextControl tc(0);
2418 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2423 void tst_qquicktextedit::readOnly()
2425 QQuickView canvas(testFileUrl("readOnly.qml"));
2427 canvas.requestActivateWindow();
2429 QVERIFY(canvas.rootObject() != 0);
2431 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2434 QTRY_VERIFY(edit->hasActiveFocus() == true);
2435 QVERIFY(edit->isReadOnly() == true);
2436 QString initial = edit->text();
2437 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2438 simulateKey(&canvas, k);
2439 simulateKey(&canvas, Qt::Key_Return);
2440 simulateKey(&canvas, Qt::Key_Space);
2441 simulateKey(&canvas, Qt::Key_Escape);
2442 QCOMPARE(edit->text(), initial);
2444 edit->setCursorPosition(3);
2445 edit->setReadOnly(false);
2446 QCOMPARE(edit->isReadOnly(), false);
2447 QCOMPARE(edit->cursorPosition(), edit->text().length());
2450 void tst_qquicktextedit::simulateKey(QWindow *view, int key, Qt::KeyboardModifiers modifiers)
2452 QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
2453 QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
2455 QGuiApplication::sendEvent(view, &press);
2456 QGuiApplication::sendEvent(view, &release);
2459 void tst_qquicktextedit::textInput()
2461 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2463 view.requestActivateWindow();
2464 QTest::qWaitForWindowShown(&view);
2465 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2466 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2468 QVERIFY(edit->hasActiveFocus() == true);
2470 // test that input method event is committed and change signal is emitted
2471 QSignalSpy spy(edit, SIGNAL(textChanged()));
2472 QInputMethodEvent event;
2473 event.setCommitString( "Hello world!", 0, 0);
2474 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2475 QCOMPARE(edit->text(), QString("Hello world!"));
2476 QCOMPARE(spy.count(), 1);
2479 // test that document and internal text attribute are in sync
2480 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(edit));
2481 QCOMPARE(editPrivate->text, QString("Hello world!"));
2483 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
2484 QGuiApplication::sendEvent(qGuiApp->focusObject(), &queryEvent);
2485 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
2487 edit->setReadOnly(true);
2488 QGuiApplication::sendEvent(edit, &queryEvent);
2489 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
2492 void tst_qquicktextedit::inputMethodUpdate()
2494 PlatformInputContext platformInputContext;
2495 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2496 inputMethodPrivate->testContext = &platformInputContext;
2498 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2500 view.requestActivateWindow();
2501 QTest::qWaitForWindowShown(&view);
2502 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2503 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2505 QVERIFY(edit->hasActiveFocus() == true);
2507 // text change even without cursor position change needs to trigger update
2508 edit->setText("test");
2509 platformInputContext.clear();
2510 edit->setText("xxxx");
2511 QVERIFY(platformInputContext.m_updateCallCount > 0);
2513 // input method event replacing text
2514 platformInputContext.clear();
2516 QInputMethodEvent inputMethodEvent;
2517 inputMethodEvent.setCommitString("y", -1, 1);
2518 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2520 QVERIFY(platformInputContext.m_updateCallCount > 0);
2522 // input method changing selection
2523 platformInputContext.clear();
2525 QList<QInputMethodEvent::Attribute> attributes;
2526 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
2527 QInputMethodEvent inputMethodEvent("", attributes);
2528 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2530 QVERIFY(edit->selectionStart() != edit->selectionEnd());
2531 QVERIFY(platformInputContext.m_updateCallCount > 0);
2533 // programmatical selections trigger update
2534 platformInputContext.clear();
2536 QCOMPARE(platformInputContext.m_updateCallCount, 1);
2539 platformInputContext.clear();
2540 QFont font = edit->font();
2541 font.setBold(!font.bold());
2542 edit->setFont(font);
2543 QVERIFY(platformInputContext.m_updateCallCount > 0);
2546 platformInputContext.clear();
2548 QInputMethodEvent inputMethodEvent;
2549 inputMethodEvent.setCommitString("y");
2550 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2552 QVERIFY(platformInputContext.m_updateCallCount > 0);
2554 // changing cursor position
2555 platformInputContext.clear();
2556 edit->setCursorPosition(0);
2557 QVERIFY(platformInputContext.m_updateCallCount > 0);
2559 // continuing with selection
2560 platformInputContext.clear();
2561 edit->moveCursorSelection(1);
2562 QVERIFY(platformInputContext.m_updateCallCount > 0);
2564 // read only disabled input method
2565 platformInputContext.clear();
2566 edit->setReadOnly(true);
2567 QVERIFY(platformInputContext.m_updateCallCount > 0);
2568 edit->setReadOnly(false);
2570 // no updates while no focus
2571 edit->setFocus(false);
2572 platformInputContext.clear();
2573 edit->setText("Foo");
2574 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2575 edit->setCursorPosition(1);
2576 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2578 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2579 edit->setReadOnly(true);
2580 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2583 void tst_qquicktextedit::openInputPanel()
2585 PlatformInputContext platformInputContext;
2586 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2587 inputMethodPrivate->testContext = &platformInputContext;
2589 QQuickView view(testFileUrl("openInputPanel.qml"));
2591 view.requestActivateWindow();
2592 QTest::qWaitForWindowShown(&view);
2593 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2595 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2598 // check default values
2599 QVERIFY(edit->focusOnPress());
2600 QVERIFY(!edit->hasActiveFocus());
2601 qDebug() << &edit << qApp->focusObject();
2602 QVERIFY(qApp->focusObject() != edit);
2604 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2606 // input panel should open on focus
2607 QPoint centerPoint(view.width()/2, view.height()/2);
2608 Qt::KeyboardModifiers noModifiers = 0;
2609 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2610 QGuiApplication::processEvents();
2611 QVERIFY(edit->hasActiveFocus());
2612 QCOMPARE(qApp->focusObject(), edit);
2613 QCOMPARE(qApp->inputMethod()->isVisible(), true);
2614 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2616 // input panel should be re-opened when pressing already focused TextEdit
2617 qApp->inputMethod()->hide();
2618 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2619 QVERIFY(edit->hasActiveFocus());
2620 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2621 QGuiApplication::processEvents();
2622 QCOMPARE(qApp->inputMethod()->isVisible(), true);
2623 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2625 // input panel should stay visible if focus is lost to another text editor
2626 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
2627 QQuickTextEdit anotherEdit;
2628 anotherEdit.setParentItem(view.rootObject());
2629 anotherEdit.setFocus(true);
2630 QCOMPARE(qApp->inputMethod()->isVisible(), true);
2631 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherEdit));
2632 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2634 anotherEdit.setFocus(false);
2635 QVERIFY(qApp->focusObject() != &anotherEdit);
2636 QCOMPARE(view.activeFocusItem(), view.rootItem());
2637 anotherEdit.setFocus(true);
2639 qApp->inputMethod()->hide();
2641 // input panel should not be opened if TextEdit is read only
2642 edit->setReadOnly(true);
2643 edit->setFocus(true);
2644 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2645 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2646 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2647 QGuiApplication::processEvents();
2648 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2650 // input panel should not be opened if focusOnPress is set to false
2651 edit->setFocusOnPress(false);
2652 edit->setFocus(false);
2653 edit->setFocus(true);
2654 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2655 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2656 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2657 QCOMPARE(qApp->inputMethod()->isVisible(), false);
2659 inputMethodPrivate->testContext = 0;
2662 void tst_qquicktextedit::geometrySignals()
2664 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
2665 QObject *o = component.create();
2667 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2668 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2672 void tst_qquicktextedit::pastingRichText_QTBUG_14003()
2674 #ifndef QT_NO_CLIPBOARD
2675 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2676 QQmlComponent component(&engine);
2677 component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2678 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(component.create());
2680 QTRY_VERIFY(obj != 0);
2681 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2683 QMimeData *mData = new QMimeData;
2684 mData->setHtml("<font color=\"red\">Hello</font>");
2685 QGuiApplication::clipboard()->setMimeData(mData);
2688 QTRY_VERIFY(obj->text() == "");
2689 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2693 void tst_qquicktextedit::implicitSize_data()
2695 QTest::addColumn<QString>("text");
2696 QTest::addColumn<QString>("wrap");
2697 QTest::addColumn<QString>("format");
2698 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap" << "TextEdit.PlainText";
2699 QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap" << "TextEdit.RichText";
2700 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap" << "TextEdit.PlainText";
2701 QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap" << "TextEdit.RichText";
2704 void tst_qquicktextedit::implicitSize()
2706 QFETCH(QString, text);
2707 QFETCH(QString, wrap);
2708 QFETCH(QString, format);
2709 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + "; textFormat: " + format + " }";
2710 QQmlComponent textComponent(&engine);
2711 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2712 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
2714 QVERIFY(textObject->width() < textObject->implicitWidth());
2715 QVERIFY(textObject->height() == textObject->implicitHeight());
2717 textObject->resetWidth();
2718 QVERIFY(textObject->width() == textObject->implicitWidth());
2719 QVERIFY(textObject->height() == textObject->implicitHeight());
2722 void tst_qquicktextedit::contentSize()
2724 QString componentStr = "import QtQuick 2.0\nTextEdit { width: 75; height: 16; font.pixelSize: 10 }";
2725 QQmlComponent textComponent(&engine);
2726 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2727 QScopedPointer<QObject> object(textComponent.create());
2728 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data());
2730 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
2732 textObject->setText("The quick red fox jumped over the lazy brown dog");
2734 QVERIFY(textObject->contentWidth() > textObject->width());
2735 QVERIFY(textObject->contentHeight() < textObject->height());
2736 QCOMPARE(spy.count(), 1);
2738 textObject->setWrapMode(QQuickTextEdit::WordWrap);
2739 QVERIFY(textObject->contentWidth() <= textObject->width());
2740 QVERIFY(textObject->contentHeight() > textObject->height());
2741 QCOMPARE(spy.count(), 2);
2743 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
2745 QVERIFY(textObject->contentWidth() > textObject->width());
2746 QVERIFY(textObject->contentHeight() > textObject->height());
2747 QCOMPARE(spy.count(), 3);
2750 void tst_qquicktextedit::implicitSizeBinding_data()
2752 implicitSize_data();
2755 void tst_qquicktextedit::implicitSizeBinding()
2757 QFETCH(QString, text);
2758 QFETCH(QString, wrap);
2759 QFETCH(QString, format);
2760 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
2761 QQmlComponent textComponent(&engine);
2762 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2763 QScopedPointer<QObject> object(textComponent.create());
2764 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data());
2766 QCOMPARE(textObject->width(), textObject->implicitWidth());
2767 QCOMPARE(textObject->height(), textObject->implicitHeight());
2769 textObject->resetWidth();
2770 QCOMPARE(textObject->width(), textObject->implicitWidth());
2771 QCOMPARE(textObject->height(), textObject->implicitHeight());
2773 textObject->resetHeight();
2774 QCOMPARE(textObject->width(), textObject->implicitWidth());
2775 QCOMPARE(textObject->height(), textObject->implicitHeight());
2778 void tst_qquicktextedit::clipRect()
2780 QQmlComponent component(&engine);
2781 component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
2782 QScopedPointer<QObject> object(component.create());
2783 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
2786 QCOMPARE(edit->clipRect().x(), qreal(0));
2787 QCOMPARE(edit->clipRect().y(), qreal(0));
2789 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width());
2790 QCOMPARE(edit->clipRect().height(), edit->height());
2792 edit->setText("Hello World");
2793 QCOMPARE(edit->clipRect().x(), qreal(0));
2794 QCOMPARE(edit->clipRect().y(), qreal(0));
2795 // XXX: TextEdit allows an extra 3 pixels boundary for the cursor beyond it's width for non
2796 // empty text. TextInput doesn't.
2797 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2798 QCOMPARE(edit->clipRect().height(), edit->height());
2800 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
2801 edit->setWidth(edit->width() / 2);
2802 QCOMPARE(edit->clipRect().x(), qreal(0));
2803 QCOMPARE(edit->clipRect().y(), qreal(0));
2804 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2805 QCOMPARE(edit->clipRect().height(), edit->height());
2807 edit->setHeight(edit->height() * 2);
2808 QCOMPARE(edit->clipRect().x(), qreal(0));
2809 QCOMPARE(edit->clipRect().y(), qreal(0));
2810 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2811 QCOMPARE(edit->clipRect().height(), edit->height());
2813 QQmlComponent cursorComponent(&engine);
2814 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
2816 edit->setCursorDelegate(&cursorComponent);
2817 edit->setCursorVisible(true);
2819 // If a cursor delegate is used it's size should determine the excess width.
2820 QCOMPARE(edit->clipRect().x(), qreal(0));
2821 QCOMPARE(edit->clipRect().y(), qreal(0));
2822 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2823 QCOMPARE(edit->clipRect().height(), edit->height());
2825 // Alignment and wrapping don't affect the clip rect.
2826 edit->setHAlign(QQuickTextEdit::AlignRight);
2827 QCOMPARE(edit->clipRect().x(), qreal(0));
2828 QCOMPARE(edit->clipRect().y(), qreal(0));
2829 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2830 QCOMPARE(edit->clipRect().height(), edit->height());
2832 edit->setWrapMode(QQuickTextEdit::Wrap);
2833 QCOMPARE(edit->clipRect().x(), qreal(0));
2834 QCOMPARE(edit->clipRect().y(), qreal(0));
2835 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2836 QCOMPARE(edit->clipRect().height(), edit->height());
2838 edit->setVAlign(QQuickTextEdit::AlignBottom);
2839 QCOMPARE(edit->clipRect().x(), qreal(0));
2840 QCOMPARE(edit->clipRect().y(), qreal(0));
2841 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2842 QCOMPARE(edit->clipRect().height(), edit->height());
2845 void tst_qquicktextedit::boundingRect()
2847 QQmlComponent component(&engine);
2848 component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
2849 QScopedPointer<QObject> object(component.create());
2850 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
2854 layout.setFont(edit->font());
2856 if (!qmlDisableDistanceField()) {
2858 option.setUseDesignMetrics(true);
2859 layout.setTextOption(option);
2861 layout.beginLayout();
2862 QTextLine line = layout.createLine();
2865 QCOMPARE(edit->boundingRect().x(), qreal(0));
2866 QCOMPARE(edit->boundingRect().y(), qreal(0));
2867 QCOMPARE(edit->boundingRect().width(), edit->cursorRectangle().width());
2868 QCOMPARE(edit->boundingRect().height(), line.height());
2870 edit->setText("Hello World");
2872 layout.setText(edit->text());
2873 layout.beginLayout();
2874 line = layout.createLine();
2877 QCOMPARE(edit->boundingRect().x(), qreal(0));
2878 QCOMPARE(edit->boundingRect().y(), qreal(0));
2879 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2880 QCOMPARE(edit->boundingRect().height(), line.height());
2882 // the size of the bounding rect shouldn't be bounded by the size of item.
2883 edit->setWidth(edit->width() / 2);
2884 QCOMPARE(edit->boundingRect().x(), qreal(0));
2885 QCOMPARE(edit->boundingRect().y(), qreal(0));
2886 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2887 QCOMPARE(edit->boundingRect().height(), line.height());
2889 edit->setHeight(edit->height() * 2);
2890 QCOMPARE(edit->boundingRect().x(), qreal(0));
2891 QCOMPARE(edit->boundingRect().y(), qreal(0));
2892 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2893 QCOMPARE(edit->boundingRect().height(), line.height());
2895 QQmlComponent cursorComponent(&engine);
2896 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
2898 edit->setCursorDelegate(&cursorComponent);
2899 edit->setCursorVisible(true);
2901 // Don't include the size of a cursor delegate as it has its own bounding rect.
2902 QCOMPARE(edit->boundingRect().x(), qreal(0));
2903 QCOMPARE(edit->boundingRect().y(), qreal(0));
2904 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
2905 QCOMPARE(edit->boundingRect().height(), line.height());
2907 edit->setHAlign(QQuickTextEdit::AlignRight);
2908 QCOMPARE(edit->boundingRect().x(), edit->width() - line.naturalTextWidth());
2909 QCOMPARE(edit->boundingRect().y(), qreal(0));
2910 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
2911 QCOMPARE(edit->boundingRect().height(), line.height());
2913 edit->setWrapMode(QQuickTextEdit::Wrap);
2914 QCOMPARE(edit->boundingRect().right(), edit->width());
2915 QCOMPARE(edit->boundingRect().y(), qreal(0));
2916 QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
2917 QVERIFY(edit->boundingRect().height() > line.height());
2919 edit->setVAlign(QQuickTextEdit::AlignBottom);
2920 QCOMPARE(edit->boundingRect().right(), edit->width());
2921 QCOMPARE(edit->boundingRect().bottom(), edit->height());
2922 QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
2923 QVERIFY(edit->boundingRect().height() > line.height());
2926 void tst_qquicktextedit::preeditCursorRectangle()
2928 QString preeditText = "super";
2930 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2932 view.requestActivateWindow();
2933 QTest::qWaitForWindowShown(&view);
2935 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2936 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2939 QQuickItem *cursor = edit->findChild<QQuickItem *>("cursor");
2942 QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
2943 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
2947 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2948 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2949 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
2951 // Verify that the micro focus rect is positioned the same for position 0 as
2952 // it would be if there was no preedit text.
2953 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2954 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, preeditText.length(), QVariant()));
2955 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
2956 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2957 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
2958 QCOMPARE(edit->cursorRectangle(), currentRect);
2959 QCOMPARE(cursor->pos(), currentRect.topLeft());
2960 QCOMPARE(currentRect, previousRect);
2962 // Verify that the micro focus rect moves to the left as the cursor position
2966 for (int i = 1; i <= 5; ++i) {
2967 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2968 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, preeditText.length(), QVariant()));
2969 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
2970 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2971 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
2972 QCOMPARE(edit->cursorRectangle(), currentRect);
2973 QCOMPARE(cursor->pos(), currentRect.topLeft());
2974 QVERIFY(previousRect.left() < currentRect.left());
2975 QCOMPARE(editSpy.count(), 1); editSpy.clear();
2976 QCOMPARE(panelSpy.count(), 1); panelSpy.clear();
2977 previousRect = currentRect;
2980 // Verify that if the cursor rectangle is updated if the pre-edit text changes
2981 // but the (non-zero) cursor position is the same.
2984 { QInputMethodEvent imEvent("wwwww", QList<QInputMethodEvent::Attribute>()
2985 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 5, 1, QVariant()));
2986 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
2987 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2988 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
2989 QCOMPARE(edit->cursorRectangle(), currentRect);
2990 QCOMPARE(cursor->pos(), currentRect.topLeft());
2991 QCOMPARE(editSpy.count(), 1);
2992 QCOMPARE(panelSpy.count(), 1);
2994 // Verify that if there is no preedit cursor then the micro focus rect is the
2995 // same as it would be if it were positioned at the end of the preedit text.
2998 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2999 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
3000 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3001 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3002 QCOMPARE(edit->cursorRectangle(), currentRect);
3003 QCOMPARE(cursor->pos(), currentRect.topLeft());
3004 QCOMPARE(currentRect, previousRect);
3005 QCOMPARE(editSpy.count(), 1);
3006 QCOMPARE(panelSpy.count(), 1);
3009 void tst_qquicktextedit::inputMethodComposing()
3011 QString text = "supercalifragisiticexpialidocious!";
3013 QQuickView view(testFileUrl("inputContext.qml"));
3015 view.requestActivateWindow();
3016 QTest::qWaitForWindowShown(&view);
3017 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3018 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
3020 QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
3021 edit->setCursorPosition(12);
3023 QCOMPARE(edit->isInputMethodComposing(), false);
3026 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3027 QGuiApplication::sendEvent(edit, &event);
3030 QCOMPARE(edit->isInputMethodComposing(), true);
3031 QCOMPARE(spy.count(), 1);
3034 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3035 QGuiApplication::sendEvent(edit, &event);
3037 QCOMPARE(spy.count(), 1);
3040 QInputMethodEvent event;
3041 QGuiApplication::sendEvent(edit, &event);
3043 QCOMPARE(edit->isInputMethodComposing(), false);
3044 QCOMPARE(spy.count(), 2);
3046 // Changing the text while not composing doesn't alter the composing state.
3047 edit->setText(text.mid(0, 16));
3048 QCOMPARE(edit->isInputMethodComposing(), false);
3049 QCOMPARE(spy.count(), 2);
3052 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3053 QGuiApplication::sendEvent(edit, &event);
3055 QCOMPARE(edit->isInputMethodComposing(), true);
3056 QCOMPARE(spy.count(), 3);
3058 // Changing the text while composing cancels composition.
3059 edit->setText(text.mid(0, 12));
3060 QCOMPARE(edit->isInputMethodComposing(), false);
3061 QCOMPARE(spy.count(), 4);
3063 { // Preedit cursor positioned outside (empty) preedit; composing.
3064 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3065 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3066 QGuiApplication::sendEvent(edit, &event);
3068 QCOMPARE(edit->isInputMethodComposing(), true);
3069 QCOMPARE(spy.count(), 5);
3071 { // Cursor hidden; composing
3072 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3073 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3074 QGuiApplication::sendEvent(edit, &event);
3076 QCOMPARE(edit->isInputMethodComposing(), true);
3077 QCOMPARE(spy.count(), 5);
3079 { // Default cursor attributes; composing.
3080 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3081 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3082 QGuiApplication::sendEvent(edit, &event);
3084 QCOMPARE(edit->isInputMethodComposing(), true);
3085 QCOMPARE(spy.count(), 5);
3087 { // Selections are persisted: not composing
3088 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3089 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 2, 4, QVariant()));
3090 QGuiApplication::sendEvent(edit, &event);
3092 QCOMPARE(edit->isInputMethodComposing(), false);
3093 QCOMPARE(spy.count(), 6);
3095 edit->setCursorPosition(0);
3097 { // Formatting applied; composing.
3098 QTextCharFormat format;
3099 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3100 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3101 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 2, 4, format));
3102 QGuiApplication::sendEvent(edit, &event);
3104 QCOMPARE(edit->isInputMethodComposing(), true);
3105 QCOMPARE(spy.count(), 7);
3108 QInputMethodEvent event;
3109 QGuiApplication::sendEvent(edit, &event);
3111 QCOMPARE(edit->isInputMethodComposing(), false);
3112 QCOMPARE(spy.count(), 8);
3115 void tst_qquicktextedit::cursorRectangleSize()
3117 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3118 QVERIFY(canvas->rootObject() != 0);
3119 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(canvas->rootObject());
3121 // make sure cursor rectangle is not at (0,0)
3124 textEdit->setCursorPosition(3);
3125 QVERIFY(textEdit != 0);
3126 textEdit->setFocus(true);
3128 canvas->requestActivateWindow();
3129 QTest::qWaitForWindowShown(canvas);
3131 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3132 qApp->sendEvent(qApp->focusObject(), &event);
3133 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3135 QRectF cursorRectFromItem = textEdit->cursorRectangle();
3136 QRectF cursorRectFromPositionToRectangle = textEdit->positionToRectangle(textEdit->cursorPosition());
3138 // item and input query cursor rectangles match
3139 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3141 // item cursor rectangle and positionToRectangle calculations match
3142 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3144 // item-canvas transform and input item transform match
3145 QCOMPARE(QQuickItemPrivate::get(textEdit)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3147 // input panel cursorRectangle property and tranformed item cursor rectangle match
3148 QRectF sceneCursorRect = QQuickItemPrivate::get(textEdit)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3149 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3154 void tst_qquicktextedit::getText_data()
3156 QTest::addColumn<QString>("text");
3157 QTest::addColumn<int>("start");
3158 QTest::addColumn<int>("end");
3159 QTest::addColumn<QString>("expectedText");
3161 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3162 const QString plainBoldText = QStringLiteral("This is some bold text");
3164 QTest::newRow("all plain text")
3166 << 0 << standard.at(0).length()
3169 QTest::newRow("plain text sub string")
3172 << standard.at(0).mid(0, 12);
3174 QTest::newRow("plain text sub string reversed")
3177 << standard.at(0).mid(0, 12);
3179 QTest::newRow("plain text cropped beginning")
3182 << standard.at(0).mid(0, 4);
3184 QTest::newRow("plain text cropped end")
3186 << 23 << standard.at(0).length() + 8
3187 << standard.at(0).mid(23);
3189 QTest::newRow("plain text cropped beginning and end")
3191 << -9 << standard.at(0).length() + 4
3194 QTest::newRow("all rich text")
3196 << 0 << plainBoldText.length()
3199 QTest::newRow("rich text sub string")
3202 << plainBoldText.mid(14, 7);
3205 void tst_qquicktextedit::getText()
3207 QFETCH(QString, text);
3210 QFETCH(QString, expectedText);
3212 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + text + "\" }";
3213 QQmlComponent textEditComponent(&engine);
3214 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3215 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3216 QVERIFY(textEdit != 0);
3218 QCOMPARE(textEdit->getText(start, end), expectedText);
3221 void tst_qquicktextedit::getFormattedText_data()
3223 QTest::addColumn<QString>("text");
3224 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3225 QTest::addColumn<int>("start");
3226 QTest::addColumn<int>("end");
3227 QTest::addColumn<QString>("expectedText");
3229 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3230 const QString plainBoldText = QStringLiteral("This is some bold text");
3232 QTest::newRow("all plain text")
3234 << QQuickTextEdit::PlainText
3235 << 0 << standard.at(0).length()
3238 QTest::newRow("plain text sub string")
3240 << QQuickTextEdit::PlainText
3242 << standard.at(0).mid(0, 12);
3244 QTest::newRow("plain text sub string reversed")
3246 << QQuickTextEdit::PlainText
3248 << standard.at(0).mid(0, 12);
3250 QTest::newRow("plain text cropped beginning")
3252 << QQuickTextEdit::PlainText
3254 << standard.at(0).mid(0, 4);
3256 QTest::newRow("plain text cropped end")
3258 << QQuickTextEdit::PlainText
3259 << 23 << standard.at(0).length() + 8
3260 << standard.at(0).mid(23);
3262 QTest::newRow("plain text cropped beginning and end")
3264 << QQuickTextEdit::PlainText
3265 << -9 << standard.at(0).length() + 4
3268 QTest::newRow("all rich (Auto) text")
3270 << QQuickTextEdit::AutoText
3271 << 0 << plainBoldText.length()
3272 << QString("This is some \\<.*\\>bold\\</.*\\> text");
3274 QTest::newRow("all rich (Rich) text")
3276 << QQuickTextEdit::RichText
3277 << 0 << plainBoldText.length()
3278 << QString("This is some \\<.*\\>bold\\</.*\\> text");
3280 QTest::newRow("all rich (Plain) text")
3282 << QQuickTextEdit::PlainText
3283 << 0 << richBoldText.length()
3286 QTest::newRow("rich (Auto) text sub string")
3288 << QQuickTextEdit::AutoText
3290 << QString("\\<.*\\>old\\</.*\\> tex");
3292 QTest::newRow("rich (Rich) text sub string")
3294 << QQuickTextEdit::RichText
3296 << QString("\\<.*\\>old\\</.*\\> tex");
3298 QTest::newRow("rich (Plain) text sub string")
3300 << QQuickTextEdit::PlainText
3302 << richBoldText.mid(17, 10);
3305 void tst_qquicktextedit::getFormattedText()
3307 QFETCH(QString, text);
3308 QFETCH(QQuickTextEdit::TextFormat, textFormat);
3311 QFETCH(QString, expectedText);
3313 QString componentStr = "import QtQuick 2.0\nTextEdit {}";
3314 QQmlComponent textEditComponent(&engine);
3315 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3316 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3317 QVERIFY(textEdit != 0);
3319 textEdit->setTextFormat(textFormat);
3320 textEdit->setText(text);
3322 if (textFormat == QQuickTextEdit::RichText
3323 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3324 QVERIFY(textEdit->getFormattedText(start, end).contains(QRegExp(expectedText)));
3326 QCOMPARE(textEdit->getFormattedText(start, end), expectedText);
3330 void tst_qquicktextedit::insert_data()
3332 QTest::addColumn<QString>("text");
3333 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3334 QTest::addColumn<int>("selectionStart");
3335 QTest::addColumn<int>("selectionEnd");
3336 QTest::addColumn<int>("insertPosition");
3337 QTest::addColumn<QString>("insertText");
3338 QTest::addColumn<QString>("expectedText");
3339 QTest::addColumn<int>("expectedSelectionStart");
3340 QTest::addColumn<int>("expectedSelectionEnd");
3341 QTest::addColumn<int>("expectedCursorPosition");
3342 QTest::addColumn<bool>("selectionChanged");
3343 QTest::addColumn<bool>("cursorPositionChanged");
3345 QTest::newRow("at cursor position (beginning)")
3346 << standard.at(0) << QQuickTextEdit::PlainText
3349 << QString("Hello") + standard.at(0)
3353 QTest::newRow("at cursor position (end)")
3354 << standard.at(0) << QQuickTextEdit::PlainText
3355 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3357 << standard.at(0) + QString("Hello")
3358 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3361 QTest::newRow("at cursor position (middle)")
3362 << standard.at(0) << QQuickTextEdit::PlainText
3365 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3369 QTest::newRow("after cursor position (beginning)")
3370 << standard.at(0) << QQuickTextEdit::PlainText
3373 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3377 QTest::newRow("before cursor position (end)")
3378 << standard.at(0) << QQuickTextEdit::PlainText
3379 << standard.at(0).length() << standard.at(0).length() << 18
3381 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3382 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3385 QTest::newRow("before cursor position (middle)")
3386 << standard.at(0) << QQuickTextEdit::PlainText
3389 << QString("Hello") + standard.at(0)
3393 QTest::newRow("after cursor position (middle)")
3394 << standard.at(0) << QQuickTextEdit::PlainText
3395 << 18 << 18 << standard.at(0).length()
3397 << standard.at(0) + QString("Hello")
3401 QTest::newRow("before selection")
3402 << standard.at(0) << QQuickTextEdit::PlainText
3405 << QString("Hello") + standard.at(0)
3409 QTest::newRow("before reversed selection")
3410 << standard.at(0) << QQuickTextEdit::PlainText
3413 << QString("Hello") + standard.at(0)
3417 QTest::newRow("after selection")
3418 << standard.at(0) << QQuickTextEdit::PlainText
3419 << 14 << 19 << standard.at(0).length()
3421 << standard.at(0) + QString("Hello")
3425 QTest::newRow("after reversed selection")
3426 << standard.at(0) << QQuickTextEdit::PlainText
3427 << 19 << 14 << standard.at(0).length()
3429 << standard.at(0) + QString("Hello")
3433 QTest::newRow("into selection")
3434 << standard.at(0) << QQuickTextEdit::PlainText
3437 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3441 QTest::newRow("into reversed selection")
3442 << standard.at(0) << QQuickTextEdit::PlainText
3445 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3449 QTest::newRow("rich text into plain text")
3450 << standard.at(0) << QQuickTextEdit::PlainText
3452 << QString("<b>Hello</b>")
3453 << QString("<b>Hello</b>") + standard.at(0)
3457 QTest::newRow("rich text into rich text")
3458 << standard.at(0) << QQuickTextEdit::RichText
3460 << QString("<b>Hello</b>")
3461 << QString("Hello") + standard.at(0)
3465 QTest::newRow("rich text into auto text")
3466 << standard.at(0) << QQuickTextEdit::AutoText
3468 << QString("<b>Hello</b>")
3469 << QString("Hello") + standard.at(0)
3473 QTest::newRow("before start")
3474 << standard.at(0) << QQuickTextEdit::PlainText
3481 QTest::newRow("past end")
3482 << standard.at(0) << QQuickTextEdit::PlainText
3483 << 0 << 0 << standard.at(0).length() + 3
3490 void tst_qquicktextedit::insert()
3492 QFETCH(QString, text);
3493 QFETCH(QQuickTextEdit::TextFormat, textFormat);
3494 QFETCH(int, selectionStart);
3495 QFETCH(int, selectionEnd);
3496 QFETCH(int, insertPosition);
3497 QFETCH(QString, insertText);
3498 QFETCH(QString, expectedText);
3499 QFETCH(int, expectedSelectionStart);
3500 QFETCH(int, expectedSelectionEnd);
3501 QFETCH(int, expectedCursorPosition);
3502 QFETCH(bool, selectionChanged);
3503 QFETCH(bool, cursorPositionChanged);
3505 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3506 QQmlComponent textEditComponent(&engine);
3507 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3508 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3509 QVERIFY(textEdit != 0);
3511 textEdit->setTextFormat(textFormat);
3512 textEdit->select(selectionStart, selectionEnd);
3514 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
3515 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3516 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3517 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3518 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3520 textEdit->insert(insertPosition, insertText);
3522 if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
3523 Qt::mightBeRichText(text) || Qt::mightBeRichText(insertText)))) {
3524 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3526 QCOMPARE(textEdit->text(), expectedText);
3529 QCOMPARE(textEdit->length(), expectedText.length());
3531 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3532 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3533 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3535 if (selectionStart > selectionEnd)
3536 qSwap(selectionStart, selectionEnd);
3538 QEXPECT_FAIL("into selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3539 QEXPECT_FAIL("into reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3540 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3541 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3542 QEXPECT_FAIL("into reversed selection", "selectionEndChanged signal not emitted", Continue);
3543 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3544 QCOMPARE(textSpy.count() > 0, text != expectedText);
3545 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3548 void tst_qquicktextedit::remove_data()
3550 QTest::addColumn<QString>("text");
3551 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3552 QTest::addColumn<int>("selectionStart");
3553 QTest::addColumn<int>("selectionEnd");
3554 QTest::addColumn<int>("removeStart");
3555 QTest::addColumn<int>("removeEnd");
3556 QTest::addColumn<QString>("expectedText");
3557 QTest::addColumn<int>("expectedSelectionStart");
3558 QTest::addColumn<int>("expectedSelectionEnd");
3559 QTest::addColumn<int>("expectedCursorPosition");
3560 QTest::addColumn<bool>("selectionChanged");
3561 QTest::addColumn<bool>("cursorPositionChanged");
3563 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3564 const QString plainBoldText = QStringLiteral("This is some bold text");
3566 QTest::newRow("from cursor position (beginning)")
3567 << standard.at(0) << QQuickTextEdit::PlainText
3570 << standard.at(0).mid(5)
3574 QTest::newRow("to cursor position (beginning)")
3575 << standard.at(0) << QQuickTextEdit::PlainText
3578 << standard.at(0).mid(5)
3582 QTest::newRow("to cursor position (end)")
3583 << standard.at(0) << QQuickTextEdit::PlainText
3584 << standard.at(0).length() << standard.at(0).length()
3585 << standard.at(0).length() << standard.at(0).length() - 5
3586 << standard.at(0).mid(0, standard.at(0).length() - 5)
3587 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3590 QTest::newRow("to cursor position (end)")
3591 << standard.at(0) << QQuickTextEdit::PlainText
3592 << standard.at(0).length() << standard.at(0).length()
3593 << standard.at(0).length() - 5 << standard.at(0).length()
3594 << standard.at(0).mid(0, standard.at(0).length() - 5)
3595 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3598 QTest::newRow("from cursor position (middle)")
3599 << standard.at(0) << QQuickTextEdit::PlainText
3602 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3606 QTest::newRow("to cursor position (middle)")
3607 << standard.at(0) << QQuickTextEdit::PlainText
3610 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3614 QTest::newRow("after cursor position (beginning)")
3615 << standard.at(0) << QQuickTextEdit::PlainText
3618 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3622 QTest::newRow("before cursor position (end)")
3623 << standard.at(0) << QQuickTextEdit::PlainText
3624 << standard.at(0).length() << standard.at(0).length()
3626 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3627 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3630 QTest::newRow("before cursor position (middle)")
3631 << standard.at(0) << QQuickTextEdit::PlainText
3634 << standard.at(0).mid(5)
3638 QTest::newRow("after cursor position (middle)")
3639 << standard.at(0) << QQuickTextEdit::PlainText
3642 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3646 QTest::newRow("before selection")
3647 << standard.at(0) << QQuickTextEdit::PlainText
3650 << standard.at(0).mid(5)
3654 QTest::newRow("before reversed selection")
3655 << standard.at(0) << QQuickTextEdit::PlainText
3658 << standard.at(0).mid(5)
3662 QTest::newRow("after selection")
3663 << standard.at(0) << QQuickTextEdit::PlainText
3665 << standard.at(0).length() - 5 << standard.at(0).length()
3666 << standard.at(0).mid(0, standard.at(0).length() - 5)
3670 QTest::newRow("after reversed selection")
3671 << standard.at(0) << QQuickTextEdit::PlainText
3673 << standard.at(0).length() - 5 << standard.at(0).length()
3674 << standard.at(0).mid(0, standard.at(0).length() - 5)
3678 QTest::newRow("from selection")
3679 << standard.at(0) << QQuickTextEdit::PlainText
3682 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3686 QTest::newRow("from reversed selection")
3687 << standard.at(0) << QQuickTextEdit::PlainText
3690 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3694 QTest::newRow("plain text cropped beginning")
3695 << standard.at(0) << QQuickTextEdit::PlainText
3698 << standard.at(0).mid(4)
3702 QTest::newRow("plain text cropped end")
3703 << standard.at(0) << QQuickTextEdit::PlainText
3705 << 23 << standard.at(0).length() + 8
3706 << standard.at(0).mid(0, 23)
3710 QTest::newRow("plain text cropped beginning and end")
3711 << standard.at(0) << QQuickTextEdit::PlainText
3713 << -9 << standard.at(0).length() + 4
3718 QTest::newRow("all rich text")
3719 << richBoldText << QQuickTextEdit::RichText
3721 << 0 << plainBoldText.length()
3726 QTest::newRow("rick text sub string")
3727 << richBoldText << QQuickTextEdit::RichText
3730 << plainBoldText.mid(0, 14) + plainBoldText.mid(21)
3735 void tst_qquicktextedit::remove()
3737 QFETCH(QString, text);
3738 QFETCH(QQuickTextEdit::TextFormat, textFormat);
3739 QFETCH(int, selectionStart);
3740 QFETCH(int, selectionEnd);
3741 QFETCH(int, removeStart);
3742 QFETCH(int, removeEnd);
3743 QFETCH(QString, expectedText);
3744 QFETCH(int, expectedSelectionStart);
3745 QFETCH(int, expectedSelectionEnd);
3746 QFETCH(int, expectedCursorPosition);
3747 QFETCH(bool, selectionChanged);
3748 QFETCH(bool, cursorPositionChanged);
3750 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3751 QQmlComponent textEditComponent(&engine);
3752 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3753 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3754 QVERIFY(textEdit != 0);
3756 textEdit->setTextFormat(textFormat);
3757 textEdit->select(selectionStart, selectionEnd);
3759 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
3760 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3761 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3762 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3763 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3765 textEdit->remove(removeStart, removeEnd);
3767 if (textFormat == QQuickTextEdit::RichText
3768 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3769 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3771 QCOMPARE(textEdit->text(), expectedText);
3773 QCOMPARE(textEdit->length(), expectedText.length());
3775 if (selectionStart > selectionEnd) //
3776 qSwap(selectionStart, selectionEnd);
3778 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3779 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3780 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3782 QEXPECT_FAIL("from selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3783 QEXPECT_FAIL("from reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3784 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3785 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3786 QEXPECT_FAIL("from reversed selection", "selectionEndChanged signal not emitted", Continue);
3787 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3788 QCOMPARE(textSpy.count() > 0, text != expectedText);
3791 if (cursorPositionChanged) //
3792 QVERIFY(cursorPositionSpy.count() > 0);
3796 void tst_qquicktextedit::keySequence_data()
3798 QTest::addColumn<QString>("text");
3799 QTest::addColumn<QKeySequence>("sequence");
3800 QTest::addColumn<int>("selectionStart");
3801 QTest::addColumn<int>("selectionEnd");
3802 QTest::addColumn<int>("cursorPosition");
3803 QTest::addColumn<QString>("expectedText");
3804 QTest::addColumn<QString>("selectedText");
3805 QTest::addColumn<Qt::Key>("layoutDirection");
3807 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3809 QTest::newRow("select all")
3810 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3811 << 44 << standard.at(0) << standard.at(0)
3812 << Qt::Key_Direction_L;
3813 QTest::newRow("select start of line")
3814 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
3815 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
3816 << Qt::Key_Direction_L;
3817 QTest::newRow("select start of block")
3818 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
3819 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
3820 << Qt::Key_Direction_L;
3821 QTest::newRow("select end of line")
3822 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3823 << 44 << standard.at(0) << standard.at(0).mid(5)
3824 << Qt::Key_Direction_L;
3825 QTest::newRow("select end of document")
3826 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3827 << 44 << standard.at(0) << standard.at(0).mid(3)
3828 << Qt::Key_Direction_L;
3829 QTest::newRow("select end of block")
3830 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3831 << 44 << standard.at(0) << standard.at(0).mid(18)
3832 << Qt::Key_Direction_L;
3833 QTest::newRow("delete end of line")
3834 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3835 << 24 << standard.at(0).mid(0, 24) << QString()
3836 << Qt::Key_Direction_L;
3837 QTest::newRow("move to start of line")
3838 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3839 << 0 << standard.at(0) << QString()
3840 << Qt::Key_Direction_L;
3841 QTest::newRow("move to start of block")
3842 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3843 << 0 << standard.at(0) << QString()
3844 << Qt::Key_Direction_L;
3845 QTest::newRow("move to next char")
3846 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3847 << 13 << standard.at(0) << QString()
3848 << Qt::Key_Direction_L;
3849 QTest::newRow("move to previous char (ltr)")
3850 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3851 << 2 << standard.at(0) << QString()
3852 << Qt::Key_Direction_L;
3853 QTest::newRow("move to previous char (rtl)")
3854 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3855 << 4 << standard.at(0) << QString()
3856 << Qt::Key_Direction_R;
3857 QTest::newRow("move to previous char with selection")
3858 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
3859 << 3 << standard.at(0) << QString()
3860 << Qt::Key_Direction_L;
3861 QTest::newRow("select next char (ltr)")
3862 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3863 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
3864 << Qt::Key_Direction_L;
3865 QTest::newRow("select next char (rtl)")
3866 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3867 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
3868 << Qt::Key_Direction_R;
3869 QTest::newRow("select previous char (ltr)")
3870 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
3871 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
3872 << Qt::Key_Direction_L;
3873 QTest::newRow("select previous char (rtl)")
3874 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
3875 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
3876 << Qt::Key_Direction_R;
3877 QTest::newRow("move to next word (ltr)")
3878 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
3879 << 10 << standard.at(0) << QString()
3880 << Qt::Key_Direction_L;
3881 QTest::newRow("move to next word (rtl)")
3882 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
3883 << 4 << standard.at(0) << QString()
3884 << Qt::Key_Direction_R;
3885 QTest::newRow("move to previous word (ltr)")
3886 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
3887 << 4 << standard.at(0) << QString()
3888 << Qt::Key_Direction_L;
3889 QTest::newRow("move to previous word (rlt)")
3890 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
3891 << 10 << standard.at(0) << QString()
3892 << Qt::Key_Direction_R;
3893 QTest::newRow("select next word")
3894 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
3895 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
3896 << Qt::Key_Direction_L;
3897 QTest::newRow("select previous word")
3898 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
3899 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
3900 << Qt::Key_Direction_L;
3901 QTest::newRow("delete (selection)")
3902 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
3903 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
3904 << Qt::Key_Direction_L;
3905 QTest::newRow("delete (no selection)")
3906 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
3907 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
3908 << Qt::Key_Direction_L;
3909 QTest::newRow("delete end of word")
3910 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
3911 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
3912 << Qt::Key_Direction_L;
3913 QTest::newRow("delete start of word")
3914 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
3915 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
3916 << Qt::Key_Direction_L;
3919 void tst_qquicktextedit::keySequence()
3921 QFETCH(QString, text);
3922 QFETCH(QKeySequence, sequence);
3923 QFETCH(int, selectionStart);
3924 QFETCH(int, selectionEnd);
3925 QFETCH(int, cursorPosition);
3926 QFETCH(QString, expectedText);
3927 QFETCH(QString, selectedText);
3928 QFETCH(Qt::Key, layoutDirection);
3930 if (sequence.isEmpty()) {
3931 QSKIP("Key sequence is undefined");
3934 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; text: \"" + text + "\" }";
3935 QQmlComponent textEditComponent(&engine);
3936 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3937 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3938 QVERIFY(textEdit != 0);
3940 QQuickCanvas canvas;
3941 textEdit->setParentItem(canvas.rootItem());
3943 canvas.requestActivateWindow();
3944 QTest::qWaitForWindowShown(&canvas);
3945 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3947 simulateKey(&canvas, layoutDirection);
3949 textEdit->select(selectionStart, selectionEnd);
3951 simulateKeys(&canvas, sequence);
3953 QCOMPARE(textEdit->cursorPosition(), cursorPosition);
3954 QCOMPARE(textEdit->text(), expectedText);
3955 QCOMPARE(textEdit->selectedText(), selectedText);
3959 #define REPLACE_UNTIL_END 1
3961 void tst_qquicktextedit::undo_data()
3963 QTest::addColumn<QStringList>("insertString");
3964 QTest::addColumn<IntList>("insertIndex");
3965 QTest::addColumn<IntList>("insertMode");
3966 QTest::addColumn<QStringList>("expectedString");
3967 QTest::addColumn<bool>("use_keys");
3969 for (int i=0; i<2; i++) {
3970 QString keys_str = "keyboard";
3971 bool use_keys = true;
3973 keys_str = "insert";
3978 IntList insertIndex;
3980 QStringList insertString;
3981 QStringList expectedString;
3984 insertMode << NORMAL;
3985 insertString << "1";
3988 insertMode << NORMAL;
3989 insertString << "5";
3992 insertMode << NORMAL;
3993 insertString << "3";
3996 insertMode << NORMAL;
3997 insertString << "2";
4000 insertMode << NORMAL;
4001 insertString << "4";
4003 expectedString << "12345";
4004 expectedString << "1235";
4005 expectedString << "135";
4006 expectedString << "15";
4007 expectedString << "";
4009 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4017 IntList insertIndex;
4019 QStringList insertString;
4020 QStringList expectedString;
4023 insertMode << NORMAL;
4024 insertString << "World"; // World
4027 insertMode << NORMAL;
4028 insertString << "Hello"; // HelloWorld
4031 insertMode << NORMAL;
4032 insertString << "Well"; // WellHelloWorld
4035 insertMode << NORMAL;
4036 insertString << "There"; // WellHelloThereWorld;
4038 expectedString << "WellHelloThereWorld";
4039 expectedString << "WellHelloWorld";
4040 expectedString << "HelloWorld";
4041 expectedString << "World";
4042 expectedString << "";
4044 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4052 IntList insertIndex;
4054 QStringList insertString;
4055 QStringList expectedString;
4058 insertMode << NORMAL;
4059 insertString << "Ensuring";
4062 insertMode << NORMAL;
4063 insertString << " instan";
4066 insertMode << NORMAL;
4067 insertString << "an ";
4070 insertMode << REPLACE_UNTIL_END;
4071 insertString << " unique instance.";
4073 expectedString << "Ensuring a unique instance.";
4074 expectedString << "Ensuring a "; // ### Not present in TextEdit.
4075 expectedString << "Ensuring an instan";
4076 expectedString << "Ensuring instan";
4077 expectedString << "";
4079 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4089 void tst_qquicktextedit::undo()
4091 QFETCH(QStringList, insertString);
4092 QFETCH(IntList, insertIndex);
4093 QFETCH(IntList, insertMode);
4094 QFETCH(QStringList, expectedString);
4096 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4097 QQmlComponent textEditComponent(&engine);
4098 textEditComponent.setData(componentStr.toLatin1(), QUrl());
4099 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4100 QVERIFY(textEdit != 0);
4102 QQuickCanvas canvas;
4103 textEdit->setParentItem(canvas.rootItem());
4105 canvas.requestActivateWindow();
4106 QTest::qWaitForWindowShown(&canvas);
4107 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4109 QVERIFY(!textEdit->canUndo());
4111 QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
4115 // STEP 1: First build up an undo history by inserting or typing some strings...
4116 for (i = 0; i < insertString.size(); ++i) {
4117 if (insertIndex[i] > -1)
4118 textEdit->setCursorPosition(insertIndex[i]);
4120 // experimental stuff
4121 if (insertMode[i] == REPLACE_UNTIL_END) {
4122 textEdit->select(insertIndex[i], insertIndex[i] + 8);
4124 // This is what I actually want...
4125 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4128 for (int j = 0; j < insertString.at(i).length(); j++)
4129 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4132 QCOMPARE(spy.count(), 1);
4134 // STEP 2: Next call undo several times and see if we can restore to the previous state
4135 for (i = 0; i < expectedString.size() - 1; ++i) {
4136 QCOMPARE(textEdit->text(), expectedString[i]);
4137 QVERIFY(textEdit->canUndo());
4141 // STEP 3: Verify that we have undone everything
4142 QVERIFY(textEdit->text().isEmpty());
4143 QVERIFY(!textEdit->canUndo());
4144 QCOMPARE(spy.count(), 2);
4147 void tst_qquicktextedit::redo_data()
4149 QTest::addColumn<QStringList>("insertString");
4150 QTest::addColumn<IntList>("insertIndex");
4151 QTest::addColumn<QStringList>("expectedString");
4154 IntList insertIndex;
4155 QStringList insertString;
4156 QStringList expectedString;
4159 insertString << "World"; // World
4161 insertString << "Hello"; // HelloWorld
4163 insertString << "Well"; // WellHelloWorld
4165 insertString << "There"; // WellHelloThereWorld;
4167 expectedString << "World";
4168 expectedString << "HelloWorld";
4169 expectedString << "WellHelloWorld";
4170 expectedString << "WellHelloThereWorld";
4172 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4176 void tst_qquicktextedit::redo()
4178 QFETCH(QStringList, insertString);
4179 QFETCH(IntList, insertIndex);
4180 QFETCH(QStringList, expectedString);
4182 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4183 QQmlComponent textEditComponent(&engine);
4184 textEditComponent.setData(componentStr.toLatin1(), QUrl());
4185 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4186 QVERIFY(textEdit != 0);
4188 QQuickCanvas canvas;
4189 textEdit->setParentItem(canvas.rootItem());
4191 canvas.requestActivateWindow();
4192 QTest::qWaitForWindowShown(&canvas);
4193 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4195 QVERIFY(!textEdit->canUndo());
4196 QVERIFY(!textEdit->canRedo());
4198 QSignalSpy spy(textEdit, SIGNAL(canRedoChanged()));
4201 // inserts the diff strings at diff positions
4202 for (i = 0; i < insertString.size(); ++i) {
4203 if (insertIndex[i] > -1)
4204 textEdit->setCursorPosition(insertIndex[i]);
4205 for (int j = 0; j < insertString.at(i).length(); j++)
4206 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4207 QVERIFY(textEdit->canUndo());
4208 QVERIFY(!textEdit->canRedo());
4211 QCOMPARE(spy.count(), 0);
4214 while (!textEdit->text().isEmpty()) {
4215 QVERIFY(textEdit->canUndo());
4217 QVERIFY(textEdit->canRedo());
4220 QCOMPARE(spy.count(), 1);
4222 for (i = 0; i < expectedString.size(); ++i) {
4223 QVERIFY(textEdit->canRedo());
4225 QCOMPARE(textEdit->text() , expectedString[i]);
4226 QVERIFY(textEdit->canUndo());
4228 QVERIFY(!textEdit->canRedo());
4229 QCOMPARE(spy.count(), 2);
4232 void tst_qquicktextedit::undo_keypressevents_data()
4234 QTest::addColumn<KeyList>("keys");
4235 QTest::addColumn<QStringList>("expectedString");
4239 QStringList expectedString;
4252 expectedString << "BEVERYAFRAID!";
4253 expectedString << "BEVERYAFRAID";
4254 expectedString << "VERYAFRAID";
4255 expectedString << "AFRAID";
4257 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4260 QStringList expectedString;
4263 keys << "1234" << Qt::Key_Home
4265 << Qt::Key_Right << Qt::Key_Right
4267 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4271 expectedString << "12";
4272 expectedString << "1234";
4274 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4277 QStringList expectedString;
4283 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4285 << QKeySequence::Undo
4286 // ### Text is selected in text input
4288 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4291 expectedString << "AB";
4292 expectedString << "AB12";
4294 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4297 QStringList expectedString;
4302 << Qt::Key_Left << Qt::Key_Left
4306 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4307 // overwriting '1234' with '5'
4309 // undoing deletion of 'AB'
4310 << QKeySequence::Undo
4311 // ### Text is selected in text input
4312 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4313 // overwriting '1234' with '6'
4316 expectedString << "ab6cd";
4317 // for versions previous to 3.2 we overwrite needed two undo operations
4318 expectedString << "ab1234cd";
4319 expectedString << "abcd";
4321 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4324 QStringList expectedString;
4329 << Qt::Key_Backspace;
4331 expectedString << "AB";
4332 expectedString << "ABC";
4334 QTest::newRow("Inserts,backspace") << keys << expectedString;
4337 QStringList expectedString;
4341 << Qt::Key_Backspace
4345 expectedString << "ABZ";
4346 expectedString << "AB";
4347 expectedString << "ABC";
4349 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4352 QStringList expectedString;
4355 keys << "123" << Qt::Key_Home
4357 << (Qt::Key_End | Qt::ShiftModifier)
4358 // overwriting '123' with 'ABC'
4361 expectedString << "ABC";
4362 // ### One operation in TextEdit.
4363 expectedString << "A";
4364 expectedString << "123";
4366 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4370 void tst_qquicktextedit::undo_keypressevents()
4372 QFETCH(KeyList, keys);
4373 QFETCH(QStringList, expectedString);
4375 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4376 QQmlComponent textEditComponent(&engine);
4377 textEditComponent.setData(componentStr.toLatin1(), QUrl());
4378 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4379 QVERIFY(textEdit != 0);
4381 QQuickCanvas canvas;
4382 textEdit->setParentItem(canvas.rootItem());
4384 canvas.requestActivateWindow();
4385 QTest::qWaitForWindowShown(&canvas);
4386 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4388 simulateKeys(&canvas, keys);
4390 for (int i = 0; i < expectedString.size(); ++i) {
4391 QCOMPARE(textEdit->text() , expectedString[i]);
4394 QVERIFY(textEdit->text().isEmpty());
4397 void tst_qquicktextedit::baseUrl()
4399 QUrl localUrl("file:///tests/text.qml");
4400 QUrl remoteUrl("http://qt.nokia.com/test.qml");
4402 QQmlComponent textComponent(&engine);
4403 textComponent.setData("import QtQuick 2.0\n TextEdit {}", localUrl);
4404 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(textComponent.create());
4406 QCOMPARE(textObject->baseUrl(), localUrl);
4408 QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
4410 textObject->setBaseUrl(localUrl);
4411 QCOMPARE(textObject->baseUrl(), localUrl);
4412 QCOMPARE(spy.count(), 0);
4414 textObject->setBaseUrl(remoteUrl);
4415 QCOMPARE(textObject->baseUrl(), remoteUrl);
4416 QCOMPARE(spy.count(), 1);
4418 textObject->resetBaseUrl();
4419 QCOMPARE(textObject->baseUrl(), localUrl);
4420 QCOMPARE(spy.count(), 2);
4423 void tst_qquicktextedit::embeddedImages_data()
4425 QTest::addColumn<QUrl>("qmlfile");
4426 QTest::addColumn<QString>("error");
4427 QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
4428 QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
4429 << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
4430 QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
4431 QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
4432 QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
4433 << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
4434 QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
4437 void tst_qquicktextedit::embeddedImages()
4439 QFETCH(QUrl, qmlfile);
4440 QFETCH(QString, error);
4442 TestHTTPServer server(42332);
4443 server.serveDirectory(testFile("http"));
4445 if (!error.isEmpty())
4446 QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
4448 QQmlComponent textComponent(&engine, qmlfile);
4449 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
4451 QVERIFY(textObject != 0);
4452 QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
4454 QPixmap pm(testFile("http/exists.png"));
4455 if (error.isEmpty()) {
4456 QCOMPARE(textObject->width(), double(pm.width()));
4457 QCOMPARE(textObject->height(), double(pm.height()));
4459 QVERIFY(16 != pm.width()); // check test is effective
4460 QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
4461 QCOMPARE(textObject->height(), 16.0);
4467 void tst_qquicktextedit::emptytags_QTBUG_22058()
4469 QQuickView canvas(testFileUrl("qtbug-22058.qml"));
4470 QVERIFY(canvas.rootObject() != 0);
4473 canvas.requestActivateWindow();
4474 QTest::qWaitForWindowShown(&canvas);
4475 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("inputField")));
4476 QVERIFY(input->hasActiveFocus());
4478 QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
4479 event.setCommitString("<b>Bold<");
4480 QGuiApplication::sendEvent(input, &event);
4481 QCOMPARE(input->text(), QString("<b>Bold<"));
4482 event.setCommitString(">");
4483 QGuiApplication::sendEvent(input, &event);
4484 QCOMPARE(input->text(), QString("<b>Bold<>"));
4487 QTEST_MAIN(tst_qquicktextedit)
4489 #include "tst_qquicktextedit.moc"