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>
59 #include <QInputMethod>
62 #include <private/qquicktextcontrol_p.h>
63 #include "../../shared/util.h"
64 #include "../../shared/platforminputcontext.h"
65 #include <private/qinputmethod_p.h>
68 #include <Carbon/Carbon.h>
72 Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode)
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();
116 void persistentSelection();
119 void isRightToLeft_data();
120 void isRightToLeft();
122 void moveCursorSelection_data();
123 void moveCursorSelection();
124 void moveCursorSelectionSequence_data();
125 void moveCursorSelectionSequence();
126 void mouseSelection_data();
127 void mouseSelection();
128 void mouseSelectionMode_data();
129 void mouseSelectionMode();
130 void dragMouseSelection();
131 void inputMethodHints();
135 void linkActivated();
137 void cursorDelegate_data();
138 void cursorDelegate();
139 void cursorVisible();
140 void delegateLoading_data();
141 void delegateLoading();
146 void canPasteEmpty();
148 void inputMethodUpdate();
149 void openInputPanel();
150 void geometrySignals();
151 void pastingRichText_QTBUG_14003();
152 void implicitSize_data();
156 void preeditCursorRectangle();
157 void inputMethodComposing();
158 void cursorRectangleSize();
162 void getFormattedText_data();
163 void getFormattedText();
169 void keySequence_data();
176 void undo_keypressevents_data();
177 void undo_keypressevents();
180 void embeddedImages();
181 void embeddedImages_data();
183 void emptytags_QTBUG_22058();
186 void simulateKeys(QWindow *window, const QList<Key> &keys);
187 void simulateKeys(QWindow *window, const QKeySequence &sequence);
189 void simulateKey(QQuickView *, int key, Qt::KeyboardModifiers modifiers = 0);
191 QStringList standard;
192 QStringList richText;
194 QStringList hAlignmentStrings;
195 QStringList vAlignmentStrings;
197 QList<Qt::Alignment> vAlignments;
198 QList<Qt::Alignment> hAlignments;
200 QStringList colorStrings;
205 typedef QList<int> IntList;
206 Q_DECLARE_METATYPE(IntList)
208 typedef QList<Key> KeyList;
209 Q_DECLARE_METATYPE(KeyList)
211 Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
213 void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
215 for (int i = 0; i < keys.count(); ++i) {
216 const int key = keys.at(i).first;
217 const int modifiers = key & Qt::KeyboardModifierMask;
218 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
220 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
221 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
223 QGuiApplication::sendEvent(window, &press);
224 QGuiApplication::sendEvent(window, &release);
228 void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &sequence)
230 for (int i = 0; i < sequence.count(); ++i) {
231 const int key = sequence[i];
232 const int modifiers = key & Qt::KeyboardModifierMask;
234 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
238 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
240 for (int i = 0; i < sequence.count(); ++i)
241 keys << Key(sequence[i], QChar());
245 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
247 for (int i = 0; i < N - 1; ++i) {
248 int key = QTest::asciiToKey(characters[i]);
249 QChar character = QLatin1Char(characters[i]);
250 keys << Key(key, character);
255 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
257 keys << Key(key, QChar());
261 tst_qquicktextedit::tst_qquicktextedit()
263 standard << "the quick brown fox jumped over the lazy dog"
264 << "the quick brown fox\n jumped over the lazy dog"
268 richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
269 << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
271 hAlignmentStrings << "AlignLeft"
275 vAlignmentStrings << "AlignTop"
279 hAlignments << Qt::AlignLeft
283 vAlignments << Qt::AlignTop
287 colorStrings << "aliceblue"
300 // need a different test to do alpha channel test
306 void tst_qquicktextedit::cleanup()
308 // ensure not even skipped tests with custom input context leave it dangling
309 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
310 inputMethodPrivate->testContext = 0;
313 void tst_qquicktextedit::text()
316 QQmlComponent texteditComponent(&engine);
317 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
318 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
320 QVERIFY(textEditObject != 0);
321 QCOMPARE(textEditObject->text(), QString(""));
322 QCOMPARE(textEditObject->length(), 0);
325 for (int i = 0; i < standard.size(); i++)
327 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
328 QQmlComponent texteditComponent(&engine);
329 texteditComponent.setData(componentStr.toLatin1(), QUrl());
330 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
332 QVERIFY(textEditObject != 0);
333 QCOMPARE(textEditObject->text(), standard.at(i));
334 QCOMPARE(textEditObject->length(), standard.at(i).length());
337 for (int i = 0; i < richText.size(); i++)
339 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
340 QQmlComponent texteditComponent(&engine);
341 texteditComponent.setData(componentStr.toLatin1(), QUrl());
343 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
345 QVERIFY(textEditObject != 0);
347 QString expected = richText.at(i);
348 expected.replace(QRegExp("\\\\(.)"),"\\1");
349 QCOMPARE(textEditObject->text(), expected);
350 QCOMPARE(textEditObject->length(), expected.length());
353 for (int i = 0; i < standard.size(); i++)
355 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + standard.at(i) + "\" }";
356 QQmlComponent texteditComponent(&engine);
357 texteditComponent.setData(componentStr.toLatin1(), QUrl());
358 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
360 QVERIFY(textEditObject != 0);
362 QString actual = textEditObject->text();
363 QString expected = standard.at(i);
364 actual.remove(QRegExp(".*<body[^>]*>"));
365 actual.remove(QRegExp("(<[^>]*>)+"));
366 expected.remove("\n");
367 QCOMPARE(actual.simplified(), expected);
368 QCOMPARE(textEditObject->length(), expected.length());
371 for (int i = 0; i < richText.size(); i++)
373 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
374 QQmlComponent texteditComponent(&engine);
375 texteditComponent.setData(componentStr.toLatin1(), QUrl());
376 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
378 QVERIFY(textEditObject != 0);
379 QString actual = textEditObject->text();
380 QString expected = richText.at(i);
381 actual.replace(QRegExp(".*<body[^>]*>"),"");
382 actual.replace(QRegExp("(<[^>]*>)+"),"<>");
383 expected.replace(QRegExp("(<[^>]*>)+"),"<>");
384 QCOMPARE(actual.simplified(),expected.simplified());
386 expected.replace("<>", " ");
387 QCOMPARE(textEditObject->length(), expected.simplified().length());
390 for (int i = 0; i < standard.size(); i++)
392 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + standard.at(i) + "\" }";
393 QQmlComponent texteditComponent(&engine);
394 texteditComponent.setData(componentStr.toLatin1(), QUrl());
395 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
397 QVERIFY(textEditObject != 0);
398 QCOMPARE(textEditObject->text(), standard.at(i));
399 QCOMPARE(textEditObject->length(), standard.at(i).length());
402 for (int i = 0; i < richText.size(); i++)
404 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + richText.at(i) + "\" }";
405 QQmlComponent texteditComponent(&engine);
406 texteditComponent.setData(componentStr.toLatin1(), QUrl());
407 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
409 QVERIFY(textEditObject != 0);
410 QString actual = textEditObject->text();
411 QString expected = richText.at(i);
412 actual.replace(QRegExp(".*<body[^>]*>"),"");
413 actual.replace(QRegExp("(<[^>]*>)+"),"<>");
414 expected.replace(QRegExp("(<[^>]*>)+"),"<>");
415 QCOMPARE(actual.simplified(),expected.simplified());
417 expected.replace("<>", " ");
418 QCOMPARE(textEditObject->length(), expected.simplified().length());
422 void tst_qquicktextedit::width()
424 // uses Font metrics to find the width for standard and document to find the width for rich
426 QQmlComponent texteditComponent(&engine);
427 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", QUrl());
428 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
430 QVERIFY(textEditObject != 0);
431 QCOMPARE(textEditObject->width(), 0.0);
434 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
436 for (int i = 0; i < standard.size(); i++)
438 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
439 QQmlComponent texteditComponent(&engine);
440 texteditComponent.setData(componentStr.toLatin1(), QUrl());
441 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
443 QString s = standard.at(i);
444 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
446 QTextLayout layout(s);
447 layout.setFont(textEditObject->font());
448 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
449 if (requiresUnhintedMetrics) {
451 option.setUseDesignMetrics(true);
452 layout.setTextOption(option);
455 layout.beginLayout();
457 QTextLine line = layout.createLine();
464 qreal metricWidth = ceil(layout.boundingRect().width());
466 QVERIFY(textEditObject != 0);
467 QCOMPARE(textEditObject->width(), qreal(metricWidth));
470 for (int i = 0; i < richText.size(); i++)
472 QTextDocument document;
473 document.setHtml(richText.at(i));
474 document.setDocumentMargin(0);
475 if (requiresUnhintedMetrics)
476 document.setUseDesignMetrics(true);
478 int documentWidth = ceil(document.idealWidth());
480 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
481 QQmlComponent texteditComponent(&engine);
482 texteditComponent.setData(componentStr.toLatin1(), QUrl());
483 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
485 QVERIFY(textEditObject != 0);
486 QCOMPARE(textEditObject->width(), qreal(documentWidth));
490 void tst_qquicktextedit::wrap()
492 // for specified width and wrap set true
494 QQmlComponent texteditComponent(&engine);
495 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
496 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
498 QVERIFY(textEditObject != 0);
499 QCOMPARE(textEditObject->width(), 300.);
502 for (int i = 0; i < standard.size(); i++)
504 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
505 QQmlComponent texteditComponent(&engine);
506 texteditComponent.setData(componentStr.toLatin1(), QUrl());
507 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
509 QVERIFY(textEditObject != 0);
510 QCOMPARE(textEditObject->width(), 300.);
513 for (int i = 0; i < richText.size(); i++)
515 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
516 QQmlComponent texteditComponent(&engine);
517 texteditComponent.setData(componentStr.toLatin1(), QUrl());
518 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
520 QVERIFY(textEditObject != 0);
521 QCOMPARE(textEditObject->width(), 300.);
526 void tst_qquicktextedit::textFormat()
529 QQmlComponent textComponent(&engine);
530 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
531 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
533 QVERIFY(textObject != 0);
534 QVERIFY(textObject->textFormat() == QQuickTextEdit::RichText);
537 QQmlComponent textComponent(&engine);
538 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
539 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
541 QVERIFY(textObject != 0);
542 QVERIFY(textObject->textFormat() == QQuickTextEdit::PlainText);
546 void tst_qquicktextedit::alignments_data()
548 QTest::addColumn<int>("hAlign");
549 QTest::addColumn<int>("vAlign");
550 QTest::addColumn<QString>("expectfile");
552 QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
553 QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
554 QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
556 QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
557 QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
558 QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
560 QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
561 QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
562 QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
566 void tst_qquicktextedit::alignments()
568 QSKIP("Image comparison of text is almost guaranteed to fail during development");
572 QFETCH(QString, expectfile);
574 QQuickView canvas(testFileUrl("alignments.qml"));
577 canvas.requestActivateWindow();
578 QTest::qWaitForWindowShown(&canvas);
579 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
581 QObject *ob = canvas.rootObject();
583 ob->setProperty("horizontalAlignment",hAlign);
584 ob->setProperty("verticalAlignment",vAlign);
585 QTRY_COMPARE(ob->property("running").toBool(),false);
586 QImage actual = canvas.grabFrameBuffer();
588 expectfile = createExpectedFileIfNotFound(expectfile, actual);
590 QImage expect(expectfile);
592 QCOMPARE(actual,expect);
596 //the alignment tests may be trivial o.oa
597 void tst_qquicktextedit::hAlign()
599 //test one align each, and then test if two align fails.
601 for (int i = 0; i < standard.size(); i++)
603 for (int j=0; j < hAlignmentStrings.size(); j++)
605 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
606 QQmlComponent texteditComponent(&engine);
607 texteditComponent.setData(componentStr.toLatin1(), QUrl());
608 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
610 QVERIFY(textEditObject != 0);
611 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
615 for (int i = 0; i < richText.size(); i++)
617 for (int j=0; j < hAlignmentStrings.size(); j++)
619 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
620 QQmlComponent texteditComponent(&engine);
621 texteditComponent.setData(componentStr.toLatin1(), QUrl());
622 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
624 QVERIFY(textEditObject != 0);
625 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
631 void tst_qquicktextedit::hAlign_RightToLeft()
633 PlatformInputContext platformInputContext;
634 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
635 inputMethodPrivate->testContext = &platformInputContext;
637 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
638 QQuickTextEdit *textEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("text");
639 QVERIFY(textEdit != 0);
642 const QString rtlText = textEdit->text();
644 // implicit alignment should follow the reading direction of text
645 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
646 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
648 // explicitly left aligned
649 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
650 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
651 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
653 // explicitly right aligned
654 textEdit->setHAlign(QQuickTextEdit::AlignRight);
655 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
656 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
658 QString textString = textEdit->text();
659 textEdit->setText(QString("<i>") + textString + QString("</i>"));
660 textEdit->resetHAlign();
662 // implicitly aligned rich text should follow the reading direction of RTL text
663 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
664 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
665 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
667 // explicitly left aligned rich text
668 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
669 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
670 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
671 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
673 // explicitly right aligned rich text
674 textEdit->setHAlign(QQuickTextEdit::AlignRight);
675 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
676 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
677 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
679 textEdit->setText(textString);
681 // explicitly center aligned
682 textEdit->setHAlign(QQuickTextEdit::AlignHCenter);
683 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignHCenter);
684 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
686 // reseted alignment should go back to following the text reading direction
687 textEdit->resetHAlign();
688 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
689 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
691 // mirror the text item
692 QQuickItemPrivate::get(textEdit)->setLayoutMirror(true);
694 // mirrored implicit alignment should continue to follow the reading direction of the text
695 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
696 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
697 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
699 // mirrored explicitly right aligned behaves as left aligned
700 textEdit->setHAlign(QQuickTextEdit::AlignRight);
701 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
702 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
703 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
705 // mirrored explicitly left aligned behaves as right aligned
706 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
707 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
708 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
709 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
712 QQuickItemPrivate::get(textEdit)->setLayoutMirror(false);
713 textEdit->resetHAlign();
715 // English text should be implicitly left aligned
716 textEdit->setText("Hello world!");
717 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
718 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
720 canvas.requestActivateWindow();
721 QTest::qWaitForWindowShown(&canvas);
722 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
724 textEdit->setText(QString());
725 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
726 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
727 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
728 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
730 // Clear pre-edit text. TextEdit should maybe do this itself on setText, but that may be
731 // redundant as an actual input method may take care of it.
732 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
734 // empty text with implicit alignment follows the system locale-based
735 // keyboard input direction from qApp->inputMethod()->inputDirection
736 textEdit->setText("");
737 platformInputContext.setInputDirection(Qt::LeftToRight);
738 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
739 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
740 QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
742 QSignalSpy cursorRectangleSpy(textEdit, SIGNAL(cursorRectangleChanged()));
744 platformInputContext.setInputDirection(Qt::RightToLeft);
745 QCOMPARE(cursorRectangleSpy.count(), 1);
746 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
747 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
748 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
750 // set input direction while having content
751 platformInputContext.setInputDirection(Qt::LeftToRight);
752 textEdit->setText("a");
753 textEdit->setCursorPosition(1);
754 platformInputContext.setInputDirection(Qt::RightToLeft);
755 QTest::keyClick(&canvas, Qt::Key_Backspace);
756 QVERIFY(textEdit->text().isEmpty());
757 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
758 QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
760 // input direction changed while not having focus
761 platformInputContext.setInputDirection(Qt::LeftToRight);
762 textEdit->setFocus(false);
763 platformInputContext.setInputDirection(Qt::RightToLeft);
764 textEdit->setFocus(true);
765 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
766 QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
768 textEdit->setHAlign(QQuickTextEdit::AlignRight);
769 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
770 QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
773 void tst_qquicktextedit::vAlign()
775 //test one align each, and then test if two align fails.
777 for (int i = 0; i < standard.size(); i++)
779 for (int j=0; j < vAlignmentStrings.size(); j++)
781 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
782 QQmlComponent texteditComponent(&engine);
783 texteditComponent.setData(componentStr.toLatin1(), QUrl());
784 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
786 QVERIFY(textEditObject != 0);
787 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
791 for (int i = 0; i < richText.size(); i++)
793 for (int j=0; j < vAlignmentStrings.size(); j++)
795 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
796 QQmlComponent texteditComponent(&engine);
797 texteditComponent.setData(componentStr.toLatin1(), QUrl());
798 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
800 QVERIFY(textEditObject != 0);
801 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
805 QQmlComponent texteditComponent(&engine);
806 texteditComponent.setData(
807 "import QtQuick 2.0\n"
808 "TextEdit { width: 100; height: 100; text: \"Hello World\" }", QUrl());
809 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
811 QVERIFY(textEditObject != 0);
813 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignTop);
814 QVERIFY(textEditObject->cursorRectangle().bottom() < 50);
815 QVERIFY(textEditObject->positionToRectangle(0).bottom() < 50);
818 textEditObject->setVAlign(QQuickTextEdit::AlignBottom);
819 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignBottom);
820 QVERIFY(textEditObject->cursorRectangle().top() > 50);
821 QVERIFY(textEditObject->positionToRectangle(0).top() > 50);
823 // explicitly center aligned
824 textEditObject->setVAlign(QQuickTextEdit::AlignVCenter);
825 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignVCenter);
826 QVERIFY(textEditObject->cursorRectangle().top() < 50);
827 QVERIFY(textEditObject->cursorRectangle().bottom() > 50);
828 QVERIFY(textEditObject->positionToRectangle(0).top() < 50);
829 QVERIFY(textEditObject->positionToRectangle(0).bottom() > 50);
832 void tst_qquicktextedit::font()
834 //test size, then bold, then italic, then family
836 QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
837 QQmlComponent texteditComponent(&engine);
838 texteditComponent.setData(componentStr.toLatin1(), QUrl());
839 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
841 QVERIFY(textEditObject != 0);
842 QCOMPARE(textEditObject->font().pointSize(), 40);
843 QCOMPARE(textEditObject->font().bold(), false);
844 QCOMPARE(textEditObject->font().italic(), false);
848 QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
849 QQmlComponent texteditComponent(&engine);
850 texteditComponent.setData(componentStr.toLatin1(), QUrl());
851 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
853 QVERIFY(textEditObject != 0);
854 QCOMPARE(textEditObject->font().bold(), true);
855 QCOMPARE(textEditObject->font().italic(), false);
859 QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
860 QQmlComponent texteditComponent(&engine);
861 texteditComponent.setData(componentStr.toLatin1(), QUrl());
862 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
864 QVERIFY(textEditObject != 0);
865 QCOMPARE(textEditObject->font().italic(), true);
866 QCOMPARE(textEditObject->font().bold(), false);
870 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
871 QQmlComponent texteditComponent(&engine);
872 texteditComponent.setData(componentStr.toLatin1(), QUrl());
873 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
875 QVERIFY(textEditObject != 0);
876 QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
877 QCOMPARE(textEditObject->font().bold(), false);
878 QCOMPARE(textEditObject->font().italic(), false);
882 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
883 QQmlComponent texteditComponent(&engine);
884 texteditComponent.setData(componentStr.toLatin1(), QUrl());
885 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
887 QVERIFY(textEditObject != 0);
888 QCOMPARE(textEditObject->font().family(), QString(""));
892 void tst_qquicktextedit::color()
896 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
897 QQmlComponent texteditComponent(&engine);
898 texteditComponent.setData(componentStr.toLatin1(), QUrl());
899 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
901 QQuickTextEditPrivate *textEditPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(textEditObject));
903 QVERIFY(textEditObject);
904 QVERIFY(textEditPrivate);
905 QVERIFY(textEditPrivate->control);
907 QPalette pal = textEditPrivate->control->palette();
908 QCOMPARE(textEditPrivate->color, QColor("black"));
909 QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text));
912 for (int i = 0; i < colorStrings.size(); i++)
914 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
915 QQmlComponent texteditComponent(&engine);
916 texteditComponent.setData(componentStr.toLatin1(), QUrl());
917 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
918 //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
919 QVERIFY(textEditObject != 0);
920 QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
924 for (int i = 0; i < colorStrings.size(); i++)
926 QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
927 QQmlComponent texteditComponent(&engine);
928 texteditComponent.setData(componentStr.toLatin1(), QUrl());
929 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
930 QVERIFY(textEditObject != 0);
931 QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
935 for (int i = 0; i < colorStrings.size(); i++)
937 QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
938 QQmlComponent texteditComponent(&engine);
939 texteditComponent.setData(componentStr.toLatin1(), QUrl());
940 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
941 QVERIFY(textEditObject != 0);
942 QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
946 QString colorStr = "#AA001234";
947 QColor testColor("#001234");
948 testColor.setAlpha(170);
950 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
951 QQmlComponent texteditComponent(&engine);
952 texteditComponent.setData(componentStr.toLatin1(), QUrl());
953 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
955 QVERIFY(textEditObject != 0);
956 QCOMPARE(textEditObject->color(), testColor);
960 void tst_qquicktextedit::textMargin()
962 for (qreal i=0; i<=10; i+=0.3) {
963 QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
964 QQmlComponent texteditComponent(&engine);
965 texteditComponent.setData(componentStr.toLatin1(), QUrl());
966 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
967 QVERIFY(textEditObject != 0);
968 QCOMPARE(textEditObject->textMargin(), i);
972 void tst_qquicktextedit::persistentSelection()
974 QQuickView canvas(testFileUrl("persistentSelection.qml"));
976 canvas.requestActivateWindow();
977 QTest::qWaitForWindowShown(&canvas);
978 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
979 canvas.requestActivateWindow();
981 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
983 QVERIFY(edit->hasActiveFocus());
985 QSignalSpy spy(edit, SIGNAL(persistentSelectionChanged(bool)));
987 QCOMPARE(edit->persistentSelection(), false);
989 edit->setPersistentSelection(false);
990 QCOMPARE(edit->persistentSelection(), false);
991 QCOMPARE(spy.count(), 0);
994 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
996 edit->setFocus(false);
997 QCOMPARE(edit->property("selected").toString(), QString());
999 edit->setFocus(true);
1000 QCOMPARE(edit->property("selected").toString(), QString());
1002 edit->setPersistentSelection(true);
1003 QCOMPARE(edit->persistentSelection(), true);
1004 QCOMPARE(spy.count(), 1);
1007 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1009 edit->setFocus(false);
1010 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1012 edit->setFocus(true);
1013 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1017 void tst_qquicktextedit::focusOnPress()
1020 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: true; text: \"Hello World\" }";
1021 QQmlComponent texteditComponent(&engine);
1022 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1023 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1024 QVERIFY(textEditObject != 0);
1025 QCOMPARE(textEditObject->focusOnPress(), true);
1029 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: false; text: \"Hello World\" }";
1030 QQmlComponent texteditComponent(&engine);
1031 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1032 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1033 QVERIFY(textEditObject != 0);
1034 QCOMPARE(textEditObject->focusOnPress(), false);
1038 void tst_qquicktextedit::selection()
1040 QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
1041 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1042 QQmlComponent texteditComponent(&engine);
1043 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1044 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1045 QVERIFY(textEditObject != 0);
1048 //Test selection follows cursor
1049 for (int i=0; i<= testStr.size(); i++) {
1050 textEditObject->setCursorPosition(i);
1051 QCOMPARE(textEditObject->cursorPosition(), i);
1052 QCOMPARE(textEditObject->selectionStart(), i);
1053 QCOMPARE(textEditObject->selectionEnd(), i);
1054 QVERIFY(textEditObject->selectedText().isNull());
1057 textEditObject->setCursorPosition(0);
1058 QVERIFY(textEditObject->cursorPosition() == 0);
1059 QVERIFY(textEditObject->selectionStart() == 0);
1060 QVERIFY(textEditObject->selectionEnd() == 0);
1061 QVERIFY(textEditObject->selectedText().isNull());
1063 // Verify invalid positions are ignored.
1064 textEditObject->setCursorPosition(-1);
1065 QVERIFY(textEditObject->cursorPosition() == 0);
1066 QVERIFY(textEditObject->selectionStart() == 0);
1067 QVERIFY(textEditObject->selectionEnd() == 0);
1068 QVERIFY(textEditObject->selectedText().isNull());
1070 textEditObject->setCursorPosition(textEditObject->text().count()+1);
1071 QVERIFY(textEditObject->cursorPosition() == 0);
1072 QVERIFY(textEditObject->selectionStart() == 0);
1073 QVERIFY(textEditObject->selectionEnd() == 0);
1074 QVERIFY(textEditObject->selectedText().isNull());
1077 for (int i=0; i<= testStr.size(); i++) {
1078 textEditObject->select(0,i);
1079 QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
1081 for (int i=0; i<= testStr.size(); i++) {
1082 textEditObject->select(i,testStr.size());
1083 QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
1086 textEditObject->setCursorPosition(0);
1087 QVERIFY(textEditObject->cursorPosition() == 0);
1088 QVERIFY(textEditObject->selectionStart() == 0);
1089 QVERIFY(textEditObject->selectionEnd() == 0);
1090 QVERIFY(textEditObject->selectedText().isNull());
1092 //Test Error Ignoring behaviour
1093 textEditObject->setCursorPosition(0);
1094 QVERIFY(textEditObject->selectedText().isNull());
1095 textEditObject->select(-10,0);
1096 QVERIFY(textEditObject->selectedText().isNull());
1097 textEditObject->select(100,101);
1098 QVERIFY(textEditObject->selectedText().isNull());
1099 textEditObject->select(0,-10);
1100 QVERIFY(textEditObject->selectedText().isNull());
1101 textEditObject->select(0,100);
1102 QVERIFY(textEditObject->selectedText().isNull());
1103 textEditObject->select(0,10);
1104 QVERIFY(textEditObject->selectedText().size() == 10);
1105 textEditObject->select(-10,0);
1106 QVERIFY(textEditObject->selectedText().size() == 10);
1107 textEditObject->select(100,101);
1108 QVERIFY(textEditObject->selectedText().size() == 10);
1109 textEditObject->select(0,-10);
1110 QVERIFY(textEditObject->selectedText().size() == 10);
1111 textEditObject->select(0,100);
1112 QVERIFY(textEditObject->selectedText().size() == 10);
1114 textEditObject->deselect();
1115 QVERIFY(textEditObject->selectedText().isNull());
1116 textEditObject->select(0,10);
1117 QVERIFY(textEditObject->selectedText().size() == 10);
1118 textEditObject->deselect();
1119 QVERIFY(textEditObject->selectedText().isNull());
1122 void tst_qquicktextedit::isRightToLeft_data()
1124 QTest::addColumn<QString>("text");
1125 QTest::addColumn<bool>("emptyString");
1126 QTest::addColumn<bool>("firstCharacter");
1127 QTest::addColumn<bool>("lastCharacter");
1128 QTest::addColumn<bool>("middleCharacter");
1129 QTest::addColumn<bool>("startString");
1130 QTest::addColumn<bool>("midString");
1131 QTest::addColumn<bool>("endString");
1133 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1134 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
1135 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
1136 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
1137 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
1138 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;
1139 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
1142 void tst_qquicktextedit::isRightToLeft()
1144 QFETCH(QString, text);
1145 QFETCH(bool, emptyString);
1146 QFETCH(bool, firstCharacter);
1147 QFETCH(bool, lastCharacter);
1148 QFETCH(bool, middleCharacter);
1149 QFETCH(bool, startString);
1150 QFETCH(bool, midString);
1151 QFETCH(bool, endString);
1153 QQuickTextEdit textEdit;
1154 textEdit.setText(text);
1156 // first test that the right string is delivered to the QString::isRightToLeft()
1157 QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
1158 QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
1159 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
1160 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
1161 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
1162 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
1164 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1165 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
1167 // then test that the feature actually works
1168 QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
1169 QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
1170 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
1171 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
1172 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
1173 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
1175 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1176 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
1179 void tst_qquicktextedit::keySelection()
1181 QQuickView canvas(testFileUrl("navigation.qml"));
1183 canvas.requestActivateWindow();
1184 QTest::qWaitForWindowShown(&canvas);
1185 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1186 canvas.requestActivateWindow();
1188 QVERIFY(canvas.rootObject() != 0);
1190 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1192 QVERIFY(input != 0);
1193 QTRY_VERIFY(input->hasActiveFocus() == true);
1195 QSignalSpy spy(input, SIGNAL(selectionChanged()));
1197 simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
1198 QVERIFY(input->hasActiveFocus() == true);
1199 QCOMPARE(input->selectedText(), QString("a"));
1200 QCOMPARE(spy.count(), 1);
1201 simulateKey(&canvas, Qt::Key_Right);
1202 QVERIFY(input->hasActiveFocus() == true);
1203 QCOMPARE(input->selectedText(), QString());
1204 QCOMPARE(spy.count(), 2);
1205 simulateKey(&canvas, Qt::Key_Right);
1206 QVERIFY(input->hasActiveFocus() == false);
1207 QCOMPARE(input->selectedText(), QString());
1208 QCOMPARE(spy.count(), 2);
1210 simulateKey(&canvas, Qt::Key_Left);
1211 QVERIFY(input->hasActiveFocus() == true);
1212 QCOMPARE(spy.count(), 2);
1213 simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1214 QVERIFY(input->hasActiveFocus() == true);
1215 QCOMPARE(input->selectedText(), QString("a"));
1216 QCOMPARE(spy.count(), 3);
1217 simulateKey(&canvas, Qt::Key_Left);
1218 QVERIFY(input->hasActiveFocus() == true);
1219 QCOMPARE(input->selectedText(), QString());
1220 QCOMPARE(spy.count(), 4);
1221 simulateKey(&canvas, Qt::Key_Left);
1222 QVERIFY(input->hasActiveFocus() == false);
1223 QCOMPARE(input->selectedText(), QString());
1224 QCOMPARE(spy.count(), 4);
1227 void tst_qquicktextedit::moveCursorSelection_data()
1229 QTest::addColumn<QString>("testStr");
1230 QTest::addColumn<int>("cursorPosition");
1231 QTest::addColumn<int>("movePosition");
1232 QTest::addColumn<QQuickTextEdit::SelectionMode>("mode");
1233 QTest::addColumn<int>("selectionStart");
1234 QTest::addColumn<int>("selectionEnd");
1235 QTest::addColumn<bool>("reversible");
1237 QTest::newRow("(t)he|characters")
1238 << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true;
1239 QTest::newRow("do(g)|characters")
1240 << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true;
1241 QTest::newRow("jum(p)ed|characters")
1242 << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true;
1243 QTest::newRow("jumped( )over|characters")
1244 << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true;
1245 QTest::newRow("(the )|characters")
1246 << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true;
1247 QTest::newRow("( dog)|characters")
1248 << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true;
1249 QTest::newRow("( jumped )|characters")
1250 << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true;
1251 QTest::newRow("th(e qu)ick|characters")
1252 << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true;
1253 QTest::newRow("la(zy d)og|characters")
1254 << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true;
1255 QTest::newRow("jum(ped ov)er|characters")
1256 << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true;
1257 QTest::newRow("()the|characters")
1258 << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true;
1259 QTest::newRow("dog()|characters")
1260 << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true;
1261 QTest::newRow("jum()ped|characters")
1262 << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true;
1264 QTest::newRow("<(t)he>|words")
1265 << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true;
1266 QTest::newRow("<do(g)>|words")
1267 << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true;
1268 QTest::newRow("<jum(p)ed>|words")
1269 << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true;
1270 QTest::newRow("<jumped( )>over|words")
1271 << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false;
1272 QTest::newRow("jumped<( )over>|words,reversed")
1273 << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false;
1274 QTest::newRow("<(the )>quick|words")
1275 << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false;
1276 QTest::newRow("<(the )quick>|words,reversed")
1277 << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false;
1278 QTest::newRow("<lazy( dog)>|words")
1279 << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false;
1280 QTest::newRow("lazy<( dog)>|words,reversed")
1281 << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false;
1282 QTest::newRow("<fox( jumped )>over|words")
1283 << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false;
1284 QTest::newRow("fox<( jumped )over>|words,reversed")
1285 << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false;
1286 QTest::newRow("<th(e qu)ick>|words")
1287 << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true;
1288 QTest::newRow("<la(zy d)og|words>")
1289 << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true;
1290 QTest::newRow("<jum(ped ov)er>|words")
1291 << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true;
1292 QTest::newRow("<()>the|words")
1293 << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1294 QTest::newRow("dog<()>|words")
1295 << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true;
1296 QTest::newRow("jum<()>ped|words")
1297 << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true;
1299 QTest::newRow("Hello<(,)> |words")
1300 << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true;
1301 QTest::newRow("Hello<(, )>world|words")
1302 << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1303 QTest::newRow("Hello<(, )world>|words,reversed")
1304 << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1305 QTest::newRow("<Hel(lo, )>world|words")
1306 << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1307 QTest::newRow("<Hel(lo, )world>|words,reversed")
1308 << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false;
1309 QTest::newRow("<Hel(lo)>,|words")
1310 << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true;
1311 QTest::newRow("Hello<()>,|words")
1312 << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true;
1313 QTest::newRow("Hello,<()>|words")
1314 << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true;
1315 QTest::newRow("Hello<,( )>world|words")
1316 << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1317 QTest::newRow("Hello,<( )world>|words,reversed")
1318 << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1319 QTest::newRow("Hello<,( world)>|words")
1320 << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1321 QTest::newRow("Hello,<( world)>|words,reversed")
1322 << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1323 QTest::newRow("Hello<,( world!)>|words")
1324 << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false;
1325 QTest::newRow("Hello,<( world!)>|words,reversed")
1326 << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1327 QTest::newRow("Hello<(, world!)>|words")
1328 << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true;
1329 QTest::newRow("world<(!)>|words")
1330 << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true;
1331 QTest::newRow("world!<()>)|words")
1332 << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true;
1333 QTest::newRow("world<()>!)|words")
1334 << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true;
1336 QTest::newRow("<(,)>olleH |words")
1337 << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true;
1338 QTest::newRow("<dlrow( ,)>olleH|words")
1339 << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1340 QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1341 << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1342 QTest::newRow("<dlrow( ,ol)leH>|words")
1343 << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false;
1344 QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1345 << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1346 QTest::newRow(",<(ol)leH>,|words")
1347 << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true;
1348 QTest::newRow(",<()>olleH|words")
1349 << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true;
1350 QTest::newRow("<()>,olleH|words")
1351 << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true;
1352 QTest::newRow("<dlrow( )>,olleH|words")
1353 << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1354 QTest::newRow("dlrow<( ),>olleH|words,reversed")
1355 << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1356 QTest::newRow("<(dlrow )>,olleH|words")
1357 << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1358 QTest::newRow("<(dlrow ),>olleH|words,reversed")
1359 << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1360 QTest::newRow("<(!dlrow )>,olleH|words")
1361 << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1362 QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1363 << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false;
1364 QTest::newRow("(!dlrow ,)olleH|words")
1365 << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true;
1366 QTest::newRow("<(!)>dlrow|words")
1367 << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true;
1368 QTest::newRow("<()>!dlrow|words")
1369 << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1370 QTest::newRow("!<()>dlrow|words")
1371 << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true;
1374 void tst_qquicktextedit::moveCursorSelection()
1376 QFETCH(QString, testStr);
1377 QFETCH(int, cursorPosition);
1378 QFETCH(int, movePosition);
1379 QFETCH(QQuickTextEdit::SelectionMode, mode);
1380 QFETCH(int, selectionStart);
1381 QFETCH(int, selectionEnd);
1382 QFETCH(bool, reversible);
1384 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1385 QQmlComponent textinputComponent(&engine);
1386 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1387 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(textinputComponent.create());
1388 QVERIFY(texteditObject != 0);
1390 texteditObject->setCursorPosition(cursorPosition);
1391 texteditObject->moveCursorSelection(movePosition, mode);
1393 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1394 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1395 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1398 texteditObject->setCursorPosition(movePosition);
1399 texteditObject->moveCursorSelection(cursorPosition, mode);
1401 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1402 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1403 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1407 void tst_qquicktextedit::moveCursorSelectionSequence_data()
1409 QTest::addColumn<QString>("testStr");
1410 QTest::addColumn<int>("cursorPosition");
1411 QTest::addColumn<int>("movePosition1");
1412 QTest::addColumn<int>("movePosition2");
1413 QTest::addColumn<int>("selection1Start");
1414 QTest::addColumn<int>("selection1End");
1415 QTest::addColumn<int>("selection2Start");
1416 QTest::addColumn<int>("selection2End");
1418 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1423 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1428 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1433 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1438 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1443 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1448 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1453 QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1458 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1463 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1468 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1473 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1478 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1483 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1488 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1493 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1498 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1503 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1509 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1514 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1520 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1525 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1532 void tst_qquicktextedit::moveCursorSelectionSequence()
1534 QFETCH(QString, testStr);
1535 QFETCH(int, cursorPosition);
1536 QFETCH(int, movePosition1);
1537 QFETCH(int, movePosition2);
1538 QFETCH(int, selection1Start);
1539 QFETCH(int, selection1End);
1540 QFETCH(int, selection2Start);
1541 QFETCH(int, selection2End);
1543 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1544 QQmlComponent texteditComponent(&engine);
1545 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1546 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1547 QVERIFY(texteditObject != 0);
1549 texteditObject->setCursorPosition(cursorPosition);
1551 texteditObject->moveCursorSelection(movePosition1, QQuickTextEdit::SelectWords);
1552 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1553 QCOMPARE(texteditObject->selectionStart(), selection1Start);
1554 QCOMPARE(texteditObject->selectionEnd(), selection1End);
1556 texteditObject->moveCursorSelection(movePosition2, QQuickTextEdit::SelectWords);
1557 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1558 QCOMPARE(texteditObject->selectionStart(), selection2Start);
1559 QCOMPARE(texteditObject->selectionEnd(), selection2End);
1563 void tst_qquicktextedit::mouseSelection_data()
1565 QTest::addColumn<QString>("qmlfile");
1566 QTest::addColumn<int>("from");
1567 QTest::addColumn<int>("to");
1568 QTest::addColumn<QString>("selectedText");
1571 QTest::newRow("on") << testFile("mouseselection_true.qml") << 4 << 9 << "45678";
1572 QTest::newRow("off") << testFile("mouseselection_false.qml") << 4 << 9 << QString();
1573 QTest::newRow("default") << testFile("mouseselection_default.qml") << 4 << 9 << QString();
1574 QTest::newRow("off word selection") << testFile("mouseselection_false_words.qml") << 4 << 9 << QString();
1575 QTest::newRow("on word selection (4,9)") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789";
1576 QTest::newRow("on word selection (2,13)") << testFile("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1577 QTest::newRow("on word selection (2,30)") << testFile("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1578 QTest::newRow("on word selection (9,13)") << testFile("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1579 QTest::newRow("on word selection (9,30)") << testFile("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1580 QTest::newRow("on word selection (13,2)") << testFile("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1581 QTest::newRow("on word selection (20,2)") << testFile("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1582 QTest::newRow("on word selection (12,9)") << testFile("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1583 QTest::newRow("on word selection (30,9)") << testFile("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1586 void tst_qquicktextedit::mouseSelection()
1588 QFETCH(QString, qmlfile);
1591 QFETCH(QString, selectedText);
1593 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1596 canvas.requestActivateWindow();
1597 QTest::qWaitForWindowShown(&canvas);
1598 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1600 QVERIFY(canvas.rootObject() != 0);
1601 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1602 QVERIFY(textEditObject != 0);
1604 // press-and-drag-and-release from x1 to x2
1605 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1606 QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1607 QTest::mousePress(&canvas, Qt::LeftButton, 0, p1);
1608 QTest::mouseMove(&canvas, p2);
1609 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2);
1611 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1613 // Clicking and shift to clicking between the same points should select the same text.
1614 textEditObject->setCursorPosition(0);
1615 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1616 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1618 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1621 void tst_qquicktextedit::dragMouseSelection()
1623 QString qmlfile = testFile("mouseselection_true.qml");
1625 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1628 canvas.requestActivateWindow();
1629 QTest::qWaitForWindowShown(&canvas);
1630 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1632 QVERIFY(canvas.rootObject() != 0);
1633 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1634 QVERIFY(textEditObject != 0);
1636 // press-and-drag-and-release from x1 to x2
1639 int y = textEditObject->height()/2;
1640 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1641 QTest::mouseMove(&canvas, QPoint(x2, y));
1642 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1645 QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1647 // press and drag the current selection.
1650 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1651 QTest::mouseMove(&canvas, QPoint(x2, y));
1652 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1655 QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1657 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1660 void tst_qquicktextedit::mouseSelectionMode_data()
1662 QTest::addColumn<QString>("qmlfile");
1663 QTest::addColumn<bool>("selectWords");
1666 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1667 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1668 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1671 void tst_qquicktextedit::mouseSelectionMode()
1673 QFETCH(QString, qmlfile);
1674 QFETCH(bool, selectWords);
1676 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1678 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1681 canvas.requestActivateWindow();
1682 QTest::qWaitForWindowShown(&canvas);
1683 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1685 QVERIFY(canvas.rootObject() != 0);
1686 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1687 QVERIFY(textEditObject != 0);
1689 // press-and-drag-and-release from x1 to x2
1692 int y = textEditObject->height()/2;
1693 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1694 QTest::mouseMove(&canvas, QPoint(x2, y));
1695 //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
1696 // QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1697 // QGuiApplication::sendEvent(&canvas, &mv);
1698 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1699 QString str = textEditObject->selectedText();
1701 QTRY_COMPARE(textEditObject->selectedText(), text);
1703 QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1704 QVERIFY(str != text);
1708 void tst_qquicktextedit::inputMethodHints()
1710 QQuickView canvas(testFileUrl("inputmethodhints.qml"));
1712 canvas.requestActivateWindow();
1714 QVERIFY(canvas.rootObject() != 0);
1715 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1716 QVERIFY(textEditObject != 0);
1717 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1718 QSignalSpy inputMethodHintSpy(textEditObject, SIGNAL(inputMethodHintsChanged()));
1719 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1720 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1721 QCOMPARE(inputMethodHintSpy.count(), 1);
1722 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1723 QCOMPARE(inputMethodHintSpy.count(), 1);
1725 QQuickTextEdit plainTextEdit;
1726 QCOMPARE(plainTextEdit.inputMethodHints(), Qt::ImhNone);
1729 void tst_qquicktextedit::positionAt()
1731 QQuickView canvas(testFileUrl("positionAt.qml"));
1732 QVERIFY(canvas.rootObject() != 0);
1734 canvas.requestActivateWindow();
1735 canvas.requestActivateWindow();
1736 QTest::qWaitForWindowShown(&canvas);
1738 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1739 QVERIFY(texteditObject != 0);
1741 QTextLayout layout(texteditObject->text());
1742 layout.setFont(texteditObject->font());
1744 if (!qmlDisableDistanceField()) {
1746 option.setUseDesignMetrics(true);
1747 layout.setTextOption(option);
1750 layout.beginLayout();
1751 QTextLine line = layout.createLine();
1754 const int y0 = line.height() / 2;
1755 const int y1 = line.height() * 3 / 2;
1757 int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
1759 int widthBegin = floor(line.cursorToX(pos - 1));
1760 int widthEnd = ceil(line.cursorToX(pos + 1));
1762 QVERIFY(widthBegin <= texteditObject->width() / 2);
1763 QVERIFY(widthEnd >= texteditObject->width() / 2);
1765 const qreal x0 = texteditObject->positionToRectangle(pos).x();
1766 const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
1768 QString preeditText = texteditObject->text().mid(0, pos);
1769 texteditObject->setText(texteditObject->text().mid(pos));
1770 texteditObject->setCursorPosition(0);
1772 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1773 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent);
1775 // Check all points within the preedit text return the same position.
1776 QCOMPARE(texteditObject->positionAt(0, y0), 0);
1777 QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
1778 QCOMPARE(texteditObject->positionAt(x0, y0), 0);
1780 // Verify positioning returns to normal after the preedit text.
1781 QCOMPARE(texteditObject->positionAt(x1, y0), 1);
1782 QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
1784 QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
1787 void tst_qquicktextedit::linkActivated()
1789 QQuickView canvas(testFileUrl("linkActivated.qml"));
1790 QVERIFY(canvas.rootObject() != 0);
1792 canvas.requestActivateWindow();
1793 QTest::qWaitForWindowShown(&canvas);
1795 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1796 QVERIFY(texteditObject != 0);
1798 QSignalSpy spy(texteditObject, SIGNAL(linkActivated(QString)));
1800 const QString link("http://example.com/");
1802 const QPointF linkPos = texteditObject->positionToRectangle(7).center();
1803 const QPointF textPos = texteditObject->positionToRectangle(2).center();
1805 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
1806 QTRY_COMPARE(spy.count(), 1);
1807 QCOMPARE(spy.last()[0].toString(), link);
1809 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
1811 QCOMPARE(spy.count(), 1);
1813 texteditObject->setReadOnly(true);
1815 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
1816 QTRY_COMPARE(spy.count(), 2);
1817 QCOMPARE(spy.last()[0].toString(), link);
1819 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
1821 QCOMPARE(spy.count(), 2);
1824 void tst_qquicktextedit::cursorDelegate_data()
1826 QTest::addColumn<QUrl>("source");
1827 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
1828 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
1829 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
1832 void tst_qquicktextedit::cursorDelegate()
1834 QFETCH(QUrl, source);
1835 QQuickView view(source);
1837 view.requestActivateWindow();
1838 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
1839 QVERIFY(textEditObject != 0);
1840 QVERIFY(textEditObject->findChild<QQuickItem*>("cursorInstance"));
1841 //Test Delegate gets created
1842 textEditObject->setFocus(true);
1843 QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>("cursorInstance");
1844 QVERIFY(delegateObject);
1845 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1846 //Test Delegate gets moved
1847 for (int i=0; i<= textEditObject->text().length(); i++) {
1848 textEditObject->setCursorPosition(i);
1849 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1850 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1852 // Clear preedit text;
1853 QInputMethodEvent event;
1854 QGuiApplication::sendEvent(&view, &event);
1857 // Test delegate gets moved on mouse press.
1858 textEditObject->setSelectByMouse(true);
1859 textEditObject->setCursorPosition(0);
1860 const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
1861 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
1863 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1864 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1865 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1867 // Test delegate gets moved on mouse drag
1868 textEditObject->setCursorPosition(0);
1869 const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
1870 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
1871 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1872 QGuiApplication::sendEvent(&view, &mv);
1873 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
1875 QTRY_COMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1876 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1878 textEditObject->setReadOnly(true);
1879 textEditObject->setCursorPosition(0);
1880 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1882 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1883 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1884 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1886 textEditObject->setCursorPosition(0);
1887 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1889 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1890 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1891 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1893 textEditObject->setCursorPosition(0);
1894 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1895 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1896 //Test Delegate gets deleted
1897 textEditObject->setCursorDelegate(0);
1898 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
1901 void tst_qquicktextedit::cursorVisible()
1903 QQuickView view(testFileUrl("cursorVisible.qml"));
1905 view.requestActivateWindow();
1906 QTest::qWaitForWindowShown(&view);
1907 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1909 QQuickTextEdit edit;
1910 QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
1912 QCOMPARE(edit.isCursorVisible(), false);
1914 edit.setCursorVisible(true);
1915 QCOMPARE(edit.isCursorVisible(), true);
1916 QCOMPARE(spy.count(), 1);
1918 edit.setCursorVisible(false);
1919 QCOMPARE(edit.isCursorVisible(), false);
1920 QCOMPARE(spy.count(), 2);
1922 edit.setFocus(true);
1923 QCOMPARE(edit.isCursorVisible(), false);
1924 QCOMPARE(spy.count(), 2);
1926 edit.setParentItem(view.rootObject());
1927 QCOMPARE(edit.isCursorVisible(), true);
1928 QCOMPARE(spy.count(), 3);
1930 edit.setFocus(false);
1931 QCOMPARE(edit.isCursorVisible(), false);
1932 QCOMPARE(spy.count(), 4);
1934 edit.setFocus(true);
1935 QCOMPARE(edit.isCursorVisible(), true);
1936 QCOMPARE(spy.count(), 5);
1938 QQuickView alternateView;
1939 alternateView.show();
1940 alternateView.requestActivateWindow();
1941 QTest::qWaitForWindowShown(&alternateView);
1943 QCOMPARE(edit.isCursorVisible(), false);
1944 QCOMPARE(spy.count(), 6);
1946 view.requestActivateWindow();
1947 QTest::qWaitForWindowShown(&view);
1948 QCOMPARE(edit.isCursorVisible(), true);
1949 QCOMPARE(spy.count(), 7);
1952 void tst_qquicktextedit::delegateLoading_data()
1954 QTest::addColumn<QString>("qmlfile");
1955 QTest::addColumn<QString>("error");
1958 QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
1959 QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
1960 QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
1963 void tst_qquicktextedit::delegateLoading()
1966 QSKIP("Test crashes during canvas tear down. QTBUG-23010");
1968 QFETCH(QString, qmlfile);
1969 QFETCH(QString, error);
1971 TestHTTPServer server(42332);
1972 server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect);
1973 server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay);
1974 server.serveDirectory(testFile("http"));
1976 QQuickView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
1978 view.requestActivateWindow();
1980 if (!error.isEmpty()) {
1981 QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
1982 QTRY_VERIFY(view.status()==QQuickView::Error);
1983 QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
1985 QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
1986 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
1987 // view.rootObject()->dumpObjectTree();
1988 QVERIFY(textEditObject != 0);
1989 textEditObject->setFocus(true);
1990 QQuickItem *delegate;
1991 delegate = view.rootObject()->findChild<QQuickItem*>("delegateOkay");
1993 delegate = view.rootObject()->findChild<QQuickItem*>("delegateSlow");
2000 //A test should be added here with a component which is ready but component.create() returns null
2001 //Not sure how to accomplish this with QQuickTextEdits cursor delegate
2002 //###This was only needed for code coverage, and could be a case of overzealous defensive programming
2003 //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB");
2004 //QVERIFY(!delegate);
2008 TextEdit element should only handle left/right keys until the cursor reaches
2009 the extent of the text, then they should ignore the keys.
2011 void tst_qquicktextedit::navigation()
2013 QQuickView canvas(testFileUrl("navigation.qml"));
2015 canvas.requestActivateWindow();
2017 QVERIFY(canvas.rootObject() != 0);
2019 QQuickItem *input = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2021 QVERIFY(input != 0);
2022 QTRY_VERIFY(input->hasActiveFocus() == true);
2023 simulateKey(&canvas, Qt::Key_Left);
2024 QVERIFY(input->hasActiveFocus() == false);
2025 simulateKey(&canvas, Qt::Key_Right);
2026 QVERIFY(input->hasActiveFocus() == true);
2027 simulateKey(&canvas, Qt::Key_Right);
2028 QVERIFY(input->hasActiveFocus() == true);
2029 simulateKey(&canvas, Qt::Key_Right);
2030 QVERIFY(input->hasActiveFocus() == false);
2031 simulateKey(&canvas, Qt::Key_Left);
2032 QVERIFY(input->hasActiveFocus() == true);
2035 void tst_qquicktextedit::copyAndPaste() {
2036 #ifndef QT_NO_CLIPBOARD
2040 PasteboardRef pasteboard;
2041 OSStatus status = PasteboardCreate(0, &pasteboard);
2042 if (status == noErr)
2043 CFRelease(pasteboard);
2045 QSKIP("This machine doesn't support the clipboard");
2049 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2050 QQmlComponent textEditComponent(&engine);
2051 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2052 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2053 QVERIFY(textEdit != 0);
2056 QCOMPARE(textEdit->text().length(), 12);
2057 textEdit->select(0, textEdit->text().length());;
2059 QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
2060 QCOMPARE(textEdit->selectedText().length(), 12);
2061 textEdit->setCursorPosition(0);
2062 QVERIFY(textEdit->canPaste());
2064 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2065 QCOMPARE(textEdit->text().length(), 24);
2068 QVERIFY(textEdit->canPaste());
2069 textEdit->setReadOnly(true);
2070 QVERIFY(!textEdit->canPaste());
2071 textEdit->setReadOnly(false);
2072 QVERIFY(textEdit->canPaste());
2075 // test that document and internal text attribute are in sync
2076 QQuickItemPrivate* pri = QQuickItemPrivate::get(textEdit);
2077 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri);
2078 QCOMPARE(textEdit->text(), editPrivate->text);
2081 textEdit->setCursorPosition(0);
2082 textEdit->selectWord();
2083 QCOMPARE(textEdit->selectedText(), QString("Hello"));
2085 // select all and cut
2086 textEdit->selectAll();
2088 QCOMPARE(textEdit->text().length(), 0);
2090 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2091 QCOMPARE(textEdit->text().length(), 24);
2095 void tst_qquicktextedit::canPaste() {
2096 #ifndef QT_NO_CLIPBOARD
2098 QGuiApplication::clipboard()->setText("Some text");
2100 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2101 QQmlComponent textEditComponent(&engine);
2102 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2103 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2104 QVERIFY(textEdit != 0);
2106 // check initial value - QTBUG-17765
2107 QQuickTextControl tc(0);
2108 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2113 void tst_qquicktextedit::canPasteEmpty() {
2114 #ifndef QT_NO_CLIPBOARD
2116 QGuiApplication::clipboard()->clear();
2118 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2119 QQmlComponent textEditComponent(&engine);
2120 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2121 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2122 QVERIFY(textEdit != 0);
2124 // check initial value - QTBUG-17765
2125 QQuickTextControl tc(0);
2126 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2131 void tst_qquicktextedit::readOnly()
2133 QQuickView canvas(testFileUrl("readOnly.qml"));
2135 canvas.requestActivateWindow();
2137 QVERIFY(canvas.rootObject() != 0);
2139 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2142 QTRY_VERIFY(edit->hasActiveFocus() == true);
2143 QVERIFY(edit->isReadOnly() == true);
2144 QString initial = edit->text();
2145 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2146 simulateKey(&canvas, k);
2147 simulateKey(&canvas, Qt::Key_Return);
2148 simulateKey(&canvas, Qt::Key_Space);
2149 simulateKey(&canvas, Qt::Key_Escape);
2150 QCOMPARE(edit->text(), initial);
2152 edit->setCursorPosition(3);
2153 edit->setReadOnly(false);
2154 QCOMPARE(edit->isReadOnly(), false);
2155 QCOMPARE(edit->cursorPosition(), edit->text().length());
2158 void tst_qquicktextedit::simulateKey(QQuickView *view, int key, Qt::KeyboardModifiers modifiers)
2160 QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
2161 QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
2163 QGuiApplication::sendEvent(view, &press);
2164 QGuiApplication::sendEvent(view, &release);
2167 void tst_qquicktextedit::textInput()
2169 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2171 view.requestActivateWindow();
2172 QTest::qWaitForWindowShown(&view);
2173 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2174 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2176 QVERIFY(edit->hasActiveFocus() == true);
2178 // test that input method event is committed and change signal is emitted
2179 QSignalSpy spy(edit, SIGNAL(textChanged()));
2180 QInputMethodEvent event;
2181 event.setCommitString( "Hello world!", 0, 0);
2182 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2183 QCOMPARE(edit->text(), QString("Hello world!"));
2184 QCOMPARE(spy.count(), 1);
2187 // test that document and internal text attribute are in sync
2188 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(edit));
2189 QCOMPARE(editPrivate->text, QString("Hello world!"));
2191 // test that tentative commit is included in text property
2194 QList<QInputMethodEvent::Attribute> attributes;
2195 QInputMethodEvent event2("preedit", attributes);
2196 event2.setTentativeCommitString("string");
2197 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event2);
2198 QCOMPARE(spy.count(), 1);
2199 QCOMPARE(edit->text(), QString("string"));
2201 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
2202 QGuiApplication::sendEvent(qGuiApp->focusObject(), &queryEvent);
2203 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
2205 edit->setReadOnly(true);
2206 QGuiApplication::sendEvent(edit, &queryEvent);
2207 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
2210 void tst_qquicktextedit::inputMethodUpdate()
2212 PlatformInputContext platformInputContext;
2213 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2214 inputMethodPrivate->testContext = &platformInputContext;
2216 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2218 view.requestActivateWindow();
2219 QTest::qWaitForWindowShown(&view);
2220 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2221 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2223 QVERIFY(edit->hasActiveFocus() == true);
2225 // text change even without cursor position change needs to trigger update
2226 edit->setText("test");
2227 platformInputContext.clear();
2228 edit->setText("xxxx");
2229 QVERIFY(platformInputContext.m_updateCallCount > 0);
2231 // input method event replacing text
2232 platformInputContext.clear();
2234 QInputMethodEvent inputMethodEvent;
2235 inputMethodEvent.setCommitString("y", -1, 1);
2236 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2238 QVERIFY(platformInputContext.m_updateCallCount > 0);
2240 // input method changing selection
2241 platformInputContext.clear();
2243 QList<QInputMethodEvent::Attribute> attributes;
2244 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
2245 QInputMethodEvent inputMethodEvent("", attributes);
2246 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2248 QVERIFY(edit->selectionStart() != edit->selectionEnd());
2249 QVERIFY(platformInputContext.m_updateCallCount > 0);
2252 platformInputContext.clear();
2253 QFont font = edit->font();
2254 font.setBold(!font.bold());
2255 edit->setFont(font);
2256 QVERIFY(platformInputContext.m_updateCallCount > 0);
2259 platformInputContext.clear();
2261 QInputMethodEvent inputMethodEvent;
2262 inputMethodEvent.setCommitString("y");
2263 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2265 QVERIFY(platformInputContext.m_updateCallCount > 0);
2267 // changing cursor position
2268 platformInputContext.clear();
2269 edit->setCursorPosition(0);
2270 QVERIFY(platformInputContext.m_updateCallCount > 0);
2272 // continuing with selection
2273 platformInputContext.clear();
2274 edit->moveCursorSelection(1);
2275 QVERIFY(platformInputContext.m_updateCallCount > 0);
2277 // read only disabled input method
2278 platformInputContext.clear();
2279 edit->setReadOnly(true);
2280 QVERIFY(platformInputContext.m_updateCallCount > 0);
2281 edit->setReadOnly(false);
2283 // no updates while no focus
2284 edit->setFocus(false);
2285 platformInputContext.clear();
2286 edit->setText("Foo");
2287 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2288 edit->setCursorPosition(1);
2289 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2291 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2292 edit->setReadOnly(true);
2293 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2296 void tst_qquicktextedit::openInputPanel()
2298 PlatformInputContext platformInputContext;
2299 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2300 inputMethodPrivate->testContext = &platformInputContext;
2302 QQuickView view(testFileUrl("openInputPanel.qml"));
2304 view.requestActivateWindow();
2305 QTest::qWaitForWindowShown(&view);
2306 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2308 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2311 // check default values
2312 QVERIFY(edit->focusOnPress());
2313 QVERIFY(!edit->hasActiveFocus());
2314 qDebug() << &edit << qApp->focusObject();
2315 QVERIFY(qApp->focusObject() != edit);
2317 QCOMPARE(qApp->inputMethod()->visible(), false);
2319 // input panel should open on focus
2320 QPoint centerPoint(view.width()/2, view.height()/2);
2321 Qt::KeyboardModifiers noModifiers = 0;
2322 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2323 QGuiApplication::processEvents();
2324 QVERIFY(edit->hasActiveFocus());
2325 QCOMPARE(qApp->focusObject(), edit);
2326 QCOMPARE(qApp->inputMethod()->visible(), true);
2327 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2329 // input panel should be re-opened when pressing already focused TextEdit
2330 qApp->inputMethod()->hide();
2331 QCOMPARE(qApp->inputMethod()->visible(), false);
2332 QVERIFY(edit->hasActiveFocus());
2333 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2334 QGuiApplication::processEvents();
2335 QCOMPARE(qApp->inputMethod()->visible(), true);
2336 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2338 // input panel should stay visible if focus is lost to another text editor
2339 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
2340 QQuickTextEdit anotherEdit;
2341 anotherEdit.setParentItem(view.rootObject());
2342 anotherEdit.setFocus(true);
2343 QCOMPARE(qApp->inputMethod()->visible(), true);
2344 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherEdit));
2345 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2347 anotherEdit.setFocus(false);
2348 QVERIFY(qApp->focusObject() != &anotherEdit);
2349 QCOMPARE(view.activeFocusItem(), view.rootItem());
2350 anotherEdit.setFocus(true);
2352 qApp->inputMethod()->hide();
2354 // input panel should not be opened if TextEdit is read only
2355 edit->setReadOnly(true);
2356 edit->setFocus(true);
2357 QCOMPARE(qApp->inputMethod()->visible(), false);
2358 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2359 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2360 QGuiApplication::processEvents();
2361 QCOMPARE(qApp->inputMethod()->visible(), false);
2363 // input panel should not be opened if focusOnPress is set to false
2364 edit->setFocusOnPress(false);
2365 edit->setFocus(false);
2366 edit->setFocus(true);
2367 QCOMPARE(qApp->inputMethod()->visible(), false);
2368 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2369 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2370 QCOMPARE(qApp->inputMethod()->visible(), false);
2372 // input panel should open when openSoftwareInputPanel is called
2373 edit->openSoftwareInputPanel();
2374 QCOMPARE(qApp->inputMethod()->visible(), true);
2376 // input panel should close when closeSoftwareInputPanel is called
2377 edit->closeSoftwareInputPanel();
2378 QCOMPARE(qApp->inputMethod()->visible(), false);
2380 inputMethodPrivate->testContext = 0;
2383 void tst_qquicktextedit::geometrySignals()
2385 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
2386 QObject *o = component.create();
2388 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2389 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2393 void tst_qquicktextedit::pastingRichText_QTBUG_14003()
2395 #ifndef QT_NO_CLIPBOARD
2396 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2397 QQmlComponent component(&engine);
2398 component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2399 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(component.create());
2401 QTRY_VERIFY(obj != 0);
2402 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2404 QMimeData *mData = new QMimeData;
2405 mData->setHtml("<font color=\"red\">Hello</font>");
2406 QGuiApplication::clipboard()->setMimeData(mData);
2409 QTRY_VERIFY(obj->text() == "");
2410 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2414 void tst_qquicktextedit::implicitSize_data()
2416 QTest::addColumn<QString>("text");
2417 QTest::addColumn<QString>("wrap");
2418 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap";
2419 QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap";
2420 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap";
2421 QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap";
2424 void tst_qquicktextedit::implicitSize()
2426 QFETCH(QString, text);
2427 QFETCH(QString, wrap);
2428 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
2429 QQmlComponent textComponent(&engine);
2430 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2431 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
2433 QVERIFY(textObject->width() < textObject->implicitWidth());
2434 QVERIFY(textObject->height() == textObject->implicitHeight());
2436 textObject->resetWidth();
2437 QVERIFY(textObject->width() == textObject->implicitWidth());
2438 QVERIFY(textObject->height() == textObject->implicitHeight());
2441 void tst_qquicktextedit::contentSize()
2443 QString componentStr = "import QtQuick 2.0\nTextEdit { width: 75; height: 16; font.pixelSize: 10 }";
2444 QQmlComponent textComponent(&engine);
2445 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2446 QScopedPointer<QObject> object(textComponent.create());
2447 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data());
2449 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
2451 textObject->setText("The quick red fox jumped over the lazy brown dog");
2453 QVERIFY(textObject->contentWidth() > textObject->width());
2454 QVERIFY(textObject->contentHeight() < textObject->height());
2455 QCOMPARE(spy.count(), 1);
2457 textObject->setWrapMode(QQuickTextEdit::WordWrap);
2458 QVERIFY(textObject->contentWidth() <= textObject->width());
2459 QVERIFY(textObject->contentHeight() > textObject->height());
2460 QCOMPARE(spy.count(), 2);
2462 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
2464 QVERIFY(textObject->contentWidth() > textObject->width());
2465 QVERIFY(textObject->contentHeight() > textObject->height());
2466 QCOMPARE(spy.count(), 3);
2469 void tst_qquicktextedit::preeditCursorRectangle()
2471 QString preeditText = "super";
2473 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2475 view.requestActivateWindow();
2476 QTest::qWaitForWindowShown(&view);
2478 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2479 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2482 QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
2483 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
2487 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2488 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2489 QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2491 // Verify that the micro focus rect is positioned the same for position 0 as
2492 // it would be if there was no preedit text.
2493 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2494 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, preeditText.length(), QVariant()));
2495 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
2496 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2497 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2498 QCOMPARE(currentRect, previousRect);
2499 QCOMPARE(editSpy.count(), 0);
2500 QCOMPARE(panelSpy.count(), 0);
2502 // Verify that the micro focus rect moves to the left as the cursor position
2504 for (int i = 1; i <= 5; ++i) {
2505 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2506 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, preeditText.length(), QVariant()));
2507 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
2508 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2509 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2510 QVERIFY(previousRect.left() < currentRect.left());
2511 QVERIFY(editSpy.count() > 0); editSpy.clear();
2512 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2513 previousRect = currentRect;
2516 // Verify that if there is no preedit cursor then the micro focus rect is the
2517 // same as it would be if it were positioned at the end of the preedit text.
2518 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
2521 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2522 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
2523 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
2524 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2525 QCOMPARE(currentRect, previousRect);
2526 QVERIFY(editSpy.count() > 0);
2527 QVERIFY(panelSpy.count() > 0);
2530 void tst_qquicktextedit::inputMethodComposing()
2532 QString text = "supercalifragisiticexpialidocious!";
2534 QQuickView view(testFileUrl("inputContext.qml"));
2536 view.requestActivateWindow();
2537 QTest::qWaitForWindowShown(&view);
2538 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2539 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2541 QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
2542 edit->setCursorPosition(12);
2544 QCOMPARE(edit->isInputMethodComposing(), false);
2547 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2548 QGuiApplication::sendEvent(edit, &event);
2551 QCOMPARE(edit->isInputMethodComposing(), true);
2552 QCOMPARE(spy.count(), 1);
2555 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2556 QGuiApplication::sendEvent(edit, &event);
2558 QCOMPARE(spy.count(), 1);
2561 QInputMethodEvent event;
2562 QGuiApplication::sendEvent(edit, &event);
2564 QCOMPARE(edit->isInputMethodComposing(), false);
2565 QCOMPARE(spy.count(), 2);
2568 void tst_qquicktextedit::cursorRectangleSize()
2570 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
2571 QVERIFY(canvas->rootObject() != 0);
2572 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(canvas->rootObject());
2574 // make sure cursor rectangle is not at (0,0)
2577 textEdit->setCursorPosition(3);
2578 QVERIFY(textEdit != 0);
2579 textEdit->setFocus(true);
2581 canvas->requestActivateWindow();
2582 QTest::qWaitForWindowShown(canvas);
2584 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2585 qApp->sendEvent(qApp->focusObject(), &event);
2586 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
2588 QRect cursorRectFromItem = textEdit->cursorRectangle();
2589 QRectF cursorRectFromPositionToRectangle = textEdit->positionToRectangle(textEdit->cursorPosition());
2591 // item and input query cursor rectangles match
2592 QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
2594 // item cursor rectangle and positionToRectangle calculations match
2595 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
2597 // item-canvas transform and input item transform match
2598 QCOMPARE(QQuickItemPrivate::get(textEdit)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
2600 // input panel cursorRectangle property and tranformed item cursor rectangle match
2601 QRectF sceneCursorRect = QQuickItemPrivate::get(textEdit)->itemToCanvasTransform().mapRect(cursorRectFromItem);
2602 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
2607 void tst_qquicktextedit::getText_data()
2609 QTest::addColumn<QString>("text");
2610 QTest::addColumn<int>("start");
2611 QTest::addColumn<int>("end");
2612 QTest::addColumn<QString>("expectedText");
2614 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
2615 const QString plainBoldText = QStringLiteral("This is some bold text");
2617 QTest::newRow("all plain text")
2619 << 0 << standard.at(0).length()
2622 QTest::newRow("plain text sub string")
2625 << standard.at(0).mid(0, 12);
2627 QTest::newRow("plain text sub string reversed")
2630 << standard.at(0).mid(0, 12);
2632 QTest::newRow("plain text cropped beginning")
2635 << standard.at(0).mid(0, 4);
2637 QTest::newRow("plain text cropped end")
2639 << 23 << standard.at(0).length() + 8
2640 << standard.at(0).mid(23);
2642 QTest::newRow("plain text cropped beginning and end")
2644 << -9 << standard.at(0).length() + 4
2647 QTest::newRow("all rich text")
2649 << 0 << plainBoldText.length()
2652 QTest::newRow("rich text sub string")
2655 << plainBoldText.mid(14, 7);
2658 void tst_qquicktextedit::getText()
2660 QFETCH(QString, text);
2663 QFETCH(QString, expectedText);
2665 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + text + "\" }";
2666 QQmlComponent textEditComponent(&engine);
2667 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2668 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2669 QVERIFY(textEdit != 0);
2671 QCOMPARE(textEdit->getText(start, end), expectedText);
2674 void tst_qquicktextedit::getFormattedText_data()
2676 QTest::addColumn<QString>("text");
2677 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
2678 QTest::addColumn<int>("start");
2679 QTest::addColumn<int>("end");
2680 QTest::addColumn<QString>("expectedText");
2682 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
2683 const QString plainBoldText = QStringLiteral("This is some bold text");
2685 QTest::newRow("all plain text")
2687 << QQuickTextEdit::PlainText
2688 << 0 << standard.at(0).length()
2691 QTest::newRow("plain text sub string")
2693 << QQuickTextEdit::PlainText
2695 << standard.at(0).mid(0, 12);
2697 QTest::newRow("plain text sub string reversed")
2699 << QQuickTextEdit::PlainText
2701 << standard.at(0).mid(0, 12);
2703 QTest::newRow("plain text cropped beginning")
2705 << QQuickTextEdit::PlainText
2707 << standard.at(0).mid(0, 4);
2709 QTest::newRow("plain text cropped end")
2711 << QQuickTextEdit::PlainText
2712 << 23 << standard.at(0).length() + 8
2713 << standard.at(0).mid(23);
2715 QTest::newRow("plain text cropped beginning and end")
2717 << QQuickTextEdit::PlainText
2718 << -9 << standard.at(0).length() + 4
2721 QTest::newRow("all rich (Auto) text")
2723 << QQuickTextEdit::AutoText
2724 << 0 << plainBoldText.length()
2725 << QString("This is some \\<.*\\>bold\\</.*\\> text");
2727 QTest::newRow("all rich (Rich) text")
2729 << QQuickTextEdit::RichText
2730 << 0 << plainBoldText.length()
2731 << QString("This is some \\<.*\\>bold\\</.*\\> text");
2733 QTest::newRow("all rich (Plain) text")
2735 << QQuickTextEdit::PlainText
2736 << 0 << richBoldText.length()
2739 QTest::newRow("rich (Auto) text sub string")
2741 << QQuickTextEdit::AutoText
2743 << QString("\\<.*\\>old\\</.*\\> tex");
2745 QTest::newRow("rich (Rich) text sub string")
2747 << QQuickTextEdit::RichText
2749 << QString("\\<.*\\>old\\</.*\\> tex");
2751 QTest::newRow("rich (Plain) text sub string")
2753 << QQuickTextEdit::PlainText
2755 << richBoldText.mid(17, 10);
2758 void tst_qquicktextedit::getFormattedText()
2760 QFETCH(QString, text);
2761 QFETCH(QQuickTextEdit::TextFormat, textFormat);
2764 QFETCH(QString, expectedText);
2766 QString componentStr = "import QtQuick 2.0\nTextEdit {}";
2767 QQmlComponent textEditComponent(&engine);
2768 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2769 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2770 QVERIFY(textEdit != 0);
2772 textEdit->setTextFormat(textFormat);
2773 textEdit->setText(text);
2775 if (textFormat == QQuickTextEdit::RichText
2776 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
2777 QVERIFY(textEdit->getFormattedText(start, end).contains(QRegExp(expectedText)));
2779 QCOMPARE(textEdit->getFormattedText(start, end), expectedText);
2783 void tst_qquicktextedit::insert_data()
2785 QTest::addColumn<QString>("text");
2786 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
2787 QTest::addColumn<int>("selectionStart");
2788 QTest::addColumn<int>("selectionEnd");
2789 QTest::addColumn<int>("insertPosition");
2790 QTest::addColumn<QString>("insertText");
2791 QTest::addColumn<QString>("expectedText");
2792 QTest::addColumn<int>("expectedSelectionStart");
2793 QTest::addColumn<int>("expectedSelectionEnd");
2794 QTest::addColumn<int>("expectedCursorPosition");
2795 QTest::addColumn<bool>("selectionChanged");
2796 QTest::addColumn<bool>("cursorPositionChanged");
2798 QTest::newRow("at cursor position (beginning)")
2799 << standard.at(0) << QQuickTextEdit::PlainText
2802 << QString("Hello") + standard.at(0)
2806 QTest::newRow("at cursor position (end)")
2807 << standard.at(0) << QQuickTextEdit::PlainText
2808 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
2810 << standard.at(0) + QString("Hello")
2811 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
2814 QTest::newRow("at cursor position (middle)")
2815 << standard.at(0) << QQuickTextEdit::PlainText
2818 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2822 QTest::newRow("after cursor position (beginning)")
2823 << standard.at(0) << QQuickTextEdit::PlainText
2826 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2830 QTest::newRow("before cursor position (end)")
2831 << standard.at(0) << QQuickTextEdit::PlainText
2832 << standard.at(0).length() << standard.at(0).length() << 18
2834 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2835 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
2838 QTest::newRow("before cursor position (middle)")
2839 << standard.at(0) << QQuickTextEdit::PlainText
2842 << QString("Hello") + standard.at(0)
2846 QTest::newRow("after cursor position (middle)")
2847 << standard.at(0) << QQuickTextEdit::PlainText
2848 << 18 << 18 << standard.at(0).length()
2850 << standard.at(0) + QString("Hello")
2854 QTest::newRow("before selection")
2855 << standard.at(0) << QQuickTextEdit::PlainText
2858 << QString("Hello") + standard.at(0)
2862 QTest::newRow("before reversed selection")
2863 << standard.at(0) << QQuickTextEdit::PlainText
2866 << QString("Hello") + standard.at(0)
2870 QTest::newRow("after selection")
2871 << standard.at(0) << QQuickTextEdit::PlainText
2872 << 14 << 19 << standard.at(0).length()
2874 << standard.at(0) + QString("Hello")
2878 QTest::newRow("after reversed selection")
2879 << standard.at(0) << QQuickTextEdit::PlainText
2880 << 19 << 14 << standard.at(0).length()
2882 << standard.at(0) + QString("Hello")
2886 QTest::newRow("into selection")
2887 << standard.at(0) << QQuickTextEdit::PlainText
2890 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2894 QTest::newRow("into reversed selection")
2895 << standard.at(0) << QQuickTextEdit::PlainText
2898 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2902 QTest::newRow("rich text into plain text")
2903 << standard.at(0) << QQuickTextEdit::PlainText
2905 << QString("<b>Hello</b>")
2906 << QString("<b>Hello</b>") + standard.at(0)
2910 QTest::newRow("rich text into rich text")
2911 << standard.at(0) << QQuickTextEdit::RichText
2913 << QString("<b>Hello</b>")
2914 << QString("Hello") + standard.at(0)
2918 QTest::newRow("rich text into auto text")
2919 << standard.at(0) << QQuickTextEdit::AutoText
2921 << QString("<b>Hello</b>")
2922 << QString("Hello") + standard.at(0)
2926 QTest::newRow("before start")
2927 << standard.at(0) << QQuickTextEdit::PlainText
2934 QTest::newRow("past end")
2935 << standard.at(0) << QQuickTextEdit::PlainText
2936 << 0 << 0 << standard.at(0).length() + 3
2943 void tst_qquicktextedit::insert()
2945 QFETCH(QString, text);
2946 QFETCH(QQuickTextEdit::TextFormat, textFormat);
2947 QFETCH(int, selectionStart);
2948 QFETCH(int, selectionEnd);
2949 QFETCH(int, insertPosition);
2950 QFETCH(QString, insertText);
2951 QFETCH(QString, expectedText);
2952 QFETCH(int, expectedSelectionStart);
2953 QFETCH(int, expectedSelectionEnd);
2954 QFETCH(int, expectedCursorPosition);
2955 QFETCH(bool, selectionChanged);
2956 QFETCH(bool, cursorPositionChanged);
2958 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
2959 QQmlComponent textEditComponent(&engine);
2960 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2961 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2962 QVERIFY(textEdit != 0);
2964 textEdit->setTextFormat(textFormat);
2965 textEdit->select(selectionStart, selectionEnd);
2967 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
2968 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
2969 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
2970 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
2971 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
2973 textEdit->insert(insertPosition, insertText);
2975 if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
2976 Qt::mightBeRichText(text) || Qt::mightBeRichText(insertText)))) {
2977 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
2979 QCOMPARE(textEdit->text(), expectedText);
2982 QCOMPARE(textEdit->length(), expectedText.length());
2984 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
2985 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
2986 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
2988 if (selectionStart > selectionEnd)
2989 qSwap(selectionStart, selectionEnd);
2991 QEXPECT_FAIL("into selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
2992 QEXPECT_FAIL("into reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
2993 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
2994 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
2995 QEXPECT_FAIL("into reversed selection", "selectionEndChanged signal not emitted", Continue);
2996 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
2997 QCOMPARE(textSpy.count() > 0, text != expectedText);
2998 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3001 void tst_qquicktextedit::remove_data()
3003 QTest::addColumn<QString>("text");
3004 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3005 QTest::addColumn<int>("selectionStart");
3006 QTest::addColumn<int>("selectionEnd");
3007 QTest::addColumn<int>("removeStart");
3008 QTest::addColumn<int>("removeEnd");
3009 QTest::addColumn<QString>("expectedText");
3010 QTest::addColumn<int>("expectedSelectionStart");
3011 QTest::addColumn<int>("expectedSelectionEnd");
3012 QTest::addColumn<int>("expectedCursorPosition");
3013 QTest::addColumn<bool>("selectionChanged");
3014 QTest::addColumn<bool>("cursorPositionChanged");
3016 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3017 const QString plainBoldText = QStringLiteral("This is some bold text");
3019 QTest::newRow("from cursor position (beginning)")
3020 << standard.at(0) << QQuickTextEdit::PlainText
3023 << standard.at(0).mid(5)
3027 QTest::newRow("to cursor position (beginning)")
3028 << standard.at(0) << QQuickTextEdit::PlainText
3031 << standard.at(0).mid(5)
3035 QTest::newRow("to cursor position (end)")
3036 << standard.at(0) << QQuickTextEdit::PlainText
3037 << standard.at(0).length() << standard.at(0).length()
3038 << standard.at(0).length() << standard.at(0).length() - 5
3039 << standard.at(0).mid(0, standard.at(0).length() - 5)
3040 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3043 QTest::newRow("to cursor position (end)")
3044 << standard.at(0) << QQuickTextEdit::PlainText
3045 << standard.at(0).length() << standard.at(0).length()
3046 << standard.at(0).length() - 5 << standard.at(0).length()
3047 << standard.at(0).mid(0, standard.at(0).length() - 5)
3048 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3051 QTest::newRow("from cursor position (middle)")
3052 << standard.at(0) << QQuickTextEdit::PlainText
3055 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3059 QTest::newRow("to cursor position (middle)")
3060 << standard.at(0) << QQuickTextEdit::PlainText
3063 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3067 QTest::newRow("after cursor position (beginning)")
3068 << standard.at(0) << QQuickTextEdit::PlainText
3071 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3075 QTest::newRow("before cursor position (end)")
3076 << standard.at(0) << QQuickTextEdit::PlainText
3077 << standard.at(0).length() << standard.at(0).length()
3079 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3080 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3083 QTest::newRow("before cursor position (middle)")
3084 << standard.at(0) << QQuickTextEdit::PlainText
3087 << standard.at(0).mid(5)
3091 QTest::newRow("after cursor position (middle)")
3092 << standard.at(0) << QQuickTextEdit::PlainText
3095 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3099 QTest::newRow("before selection")
3100 << standard.at(0) << QQuickTextEdit::PlainText
3103 << standard.at(0).mid(5)
3107 QTest::newRow("before reversed selection")
3108 << standard.at(0) << QQuickTextEdit::PlainText
3111 << standard.at(0).mid(5)
3115 QTest::newRow("after selection")
3116 << standard.at(0) << QQuickTextEdit::PlainText
3118 << standard.at(0).length() - 5 << standard.at(0).length()
3119 << standard.at(0).mid(0, standard.at(0).length() - 5)
3123 QTest::newRow("after reversed selection")
3124 << standard.at(0) << QQuickTextEdit::PlainText
3126 << standard.at(0).length() - 5 << standard.at(0).length()
3127 << standard.at(0).mid(0, standard.at(0).length() - 5)
3131 QTest::newRow("from selection")
3132 << standard.at(0) << QQuickTextEdit::PlainText
3135 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3139 QTest::newRow("from reversed selection")
3140 << standard.at(0) << QQuickTextEdit::PlainText
3143 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3147 QTest::newRow("plain text cropped beginning")
3148 << standard.at(0) << QQuickTextEdit::PlainText
3151 << standard.at(0).mid(4)
3155 QTest::newRow("plain text cropped end")
3156 << standard.at(0) << QQuickTextEdit::PlainText
3158 << 23 << standard.at(0).length() + 8
3159 << standard.at(0).mid(0, 23)
3163 QTest::newRow("plain text cropped beginning and end")
3164 << standard.at(0) << QQuickTextEdit::PlainText
3166 << -9 << standard.at(0).length() + 4
3171 QTest::newRow("all rich text")
3172 << richBoldText << QQuickTextEdit::RichText
3174 << 0 << plainBoldText.length()
3179 QTest::newRow("rick text sub string")
3180 << richBoldText << QQuickTextEdit::RichText
3183 << plainBoldText.mid(0, 14) + plainBoldText.mid(21)
3188 void tst_qquicktextedit::remove()
3190 QFETCH(QString, text);
3191 QFETCH(QQuickTextEdit::TextFormat, textFormat);
3192 QFETCH(int, selectionStart);
3193 QFETCH(int, selectionEnd);
3194 QFETCH(int, removeStart);
3195 QFETCH(int, removeEnd);
3196 QFETCH(QString, expectedText);
3197 QFETCH(int, expectedSelectionStart);
3198 QFETCH(int, expectedSelectionEnd);
3199 QFETCH(int, expectedCursorPosition);
3200 QFETCH(bool, selectionChanged);
3201 QFETCH(bool, cursorPositionChanged);
3203 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3204 QQmlComponent textEditComponent(&engine);
3205 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3206 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3207 QVERIFY(textEdit != 0);
3209 textEdit->setTextFormat(textFormat);
3210 textEdit->select(selectionStart, selectionEnd);
3212 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
3213 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3214 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3215 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3216 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3218 textEdit->remove(removeStart, removeEnd);
3220 if (textFormat == QQuickTextEdit::RichText
3221 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3222 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3224 QCOMPARE(textEdit->text(), expectedText);
3226 QCOMPARE(textEdit->length(), expectedText.length());
3228 if (selectionStart > selectionEnd) //
3229 qSwap(selectionStart, selectionEnd);
3231 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3232 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3233 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3235 QEXPECT_FAIL("from selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3236 QEXPECT_FAIL("from reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3237 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3238 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3239 QEXPECT_FAIL("from reversed selection", "selectionEndChanged signal not emitted", Continue);
3240 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3241 QCOMPARE(textSpy.count() > 0, text != expectedText);
3244 if (cursorPositionChanged) //
3245 QVERIFY(cursorPositionSpy.count() > 0);
3249 void tst_qquicktextedit::keySequence_data()
3251 QTest::addColumn<QString>("text");
3252 QTest::addColumn<QKeySequence>("sequence");
3253 QTest::addColumn<int>("selectionStart");
3254 QTest::addColumn<int>("selectionEnd");
3255 QTest::addColumn<int>("cursorPosition");
3256 QTest::addColumn<QString>("expectedText");
3257 QTest::addColumn<QString>("selectedText");
3259 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3261 QTest::newRow("select all")
3262 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3263 << 44 << standard.at(0) << standard.at(0);
3264 QTest::newRow("select end of line")
3265 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3266 << 44 << standard.at(0) << standard.at(0).mid(5);
3267 QTest::newRow("select end of document")
3268 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3269 << 44 << standard.at(0) << standard.at(0).mid(3);
3270 QTest::newRow("select end of block")
3271 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3272 << 44 << standard.at(0) << standard.at(0).mid(18);
3273 QTest::newRow("delete end of line")
3274 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3275 << 24 << standard.at(0).mid(0, 24) << QString();
3276 QTest::newRow("move to start of line")
3277 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3278 << 0 << standard.at(0) << QString();
3279 QTest::newRow("move to start of block")
3280 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3281 << 0 << standard.at(0) << QString();
3282 QTest::newRow("move to next char")
3283 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3284 << 13 << standard.at(0) << QString();
3285 QTest::newRow("move to previous char")
3286 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3287 << 2 << standard.at(0) << QString();
3288 QTest::newRow("select next char")
3289 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3290 << 24 << standard.at(0) << standard.at(0).mid(23, 1);
3291 QTest::newRow("select previous char")
3292 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
3293 << 18 << standard.at(0) << standard.at(0).mid(18, 1);
3294 QTest::newRow("move to next word")
3295 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
3296 << 10 << standard.at(0) << QString();
3297 QTest::newRow("move to previous word")
3298 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
3299 << 4 << standard.at(0) << QString();
3300 QTest::newRow("select previous word")
3301 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
3302 << 10 << standard.at(0) << standard.at(0).mid(10, 1);
3303 QTest::newRow("delete (selection)")
3304 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
3305 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
3306 QTest::newRow("delete (no selection)")
3307 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
3308 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
3309 QTest::newRow("delete end of word")
3310 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
3311 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
3312 QTest::newRow("delete start of word")
3313 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
3314 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
3317 void tst_qquicktextedit::keySequence()
3319 QFETCH(QString, text);
3320 QFETCH(QKeySequence, sequence);
3321 QFETCH(int, selectionStart);
3322 QFETCH(int, selectionEnd);
3323 QFETCH(int, cursorPosition);
3324 QFETCH(QString, expectedText);
3325 QFETCH(QString, selectedText);
3327 if (sequence.isEmpty()) {
3328 QSKIP("Key sequence is undefined");
3331 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; text: \"" + text + "\" }";
3332 QQmlComponent textEditComponent(&engine);
3333 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3334 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3335 QVERIFY(textEdit != 0);
3337 QQuickCanvas canvas;
3338 textEdit->setParentItem(canvas.rootItem());
3340 canvas.requestActivateWindow();
3341 QTest::qWaitForWindowShown(&canvas);
3342 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3344 textEdit->select(selectionStart, selectionEnd);
3346 simulateKeys(&canvas, sequence);
3348 QCOMPARE(textEdit->cursorPosition(), cursorPosition);
3349 QCOMPARE(textEdit->text(), expectedText);
3350 QCOMPARE(textEdit->selectedText(), selectedText);
3354 #define REPLACE_UNTIL_END 1
3356 void tst_qquicktextedit::undo_data()
3358 QTest::addColumn<QStringList>("insertString");
3359 QTest::addColumn<IntList>("insertIndex");
3360 QTest::addColumn<IntList>("insertMode");
3361 QTest::addColumn<QStringList>("expectedString");
3362 QTest::addColumn<bool>("use_keys");
3364 for (int i=0; i<2; i++) {
3365 QString keys_str = "keyboard";
3366 bool use_keys = true;
3368 keys_str = "insert";
3373 IntList insertIndex;
3375 QStringList insertString;
3376 QStringList expectedString;
3379 insertMode << NORMAL;
3380 insertString << "1";
3383 insertMode << NORMAL;
3384 insertString << "5";
3387 insertMode << NORMAL;
3388 insertString << "3";
3391 insertMode << NORMAL;
3392 insertString << "2";
3395 insertMode << NORMAL;
3396 insertString << "4";
3398 expectedString << "12345";
3399 expectedString << "1235";
3400 expectedString << "135";
3401 expectedString << "15";
3402 expectedString << "";
3404 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
3412 IntList insertIndex;
3414 QStringList insertString;
3415 QStringList expectedString;
3418 insertMode << NORMAL;
3419 insertString << "World"; // World
3422 insertMode << NORMAL;
3423 insertString << "Hello"; // HelloWorld
3426 insertMode << NORMAL;
3427 insertString << "Well"; // WellHelloWorld
3430 insertMode << NORMAL;
3431 insertString << "There"; // WellHelloThereWorld;
3433 expectedString << "WellHelloThereWorld";
3434 expectedString << "WellHelloWorld";
3435 expectedString << "HelloWorld";
3436 expectedString << "World";
3437 expectedString << "";
3439 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
3447 IntList insertIndex;
3449 QStringList insertString;
3450 QStringList expectedString;
3453 insertMode << NORMAL;
3454 insertString << "Ensuring";
3457 insertMode << NORMAL;
3458 insertString << " instan";
3461 insertMode << NORMAL;
3462 insertString << "an ";
3465 insertMode << REPLACE_UNTIL_END;
3466 insertString << " unique instance.";
3468 expectedString << "Ensuring a unique instance.";
3469 expectedString << "Ensuring a "; // ### Not present in TextInput.
3470 expectedString << "Ensuring an instan";
3471 expectedString << "Ensuring instan";
3472 expectedString << "";
3474 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
3484 void tst_qquicktextedit::undo()
3486 QFETCH(QStringList, insertString);
3487 QFETCH(IntList, insertIndex);
3488 QFETCH(IntList, insertMode);
3489 QFETCH(QStringList, expectedString);
3491 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3492 QQmlComponent textEditComponent(&engine);
3493 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3494 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3495 QVERIFY(textEdit != 0);
3497 QQuickCanvas canvas;
3498 textEdit->setParentItem(canvas.rootItem());
3500 canvas.requestActivateWindow();
3501 QTest::qWaitForWindowShown(&canvas);
3502 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3504 QVERIFY(!textEdit->canUndo());
3506 QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
3510 // STEP 1: First build up an undo history by inserting or typing some strings...
3511 for (i = 0; i < insertString.size(); ++i) {
3512 if (insertIndex[i] > -1)
3513 textEdit->setCursorPosition(insertIndex[i]);
3515 // experimental stuff
3516 if (insertMode[i] == REPLACE_UNTIL_END) {
3517 textEdit->select(insertIndex[i], insertIndex[i] + 8);
3519 // This is what I actually want...
3520 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
3523 for (int j = 0; j < insertString.at(i).length(); j++)
3524 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3527 QCOMPARE(spy.count(), 1);
3529 // STEP 2: Next call undo several times and see if we can restore to the previous state
3530 for (i = 0; i < expectedString.size() - 1; ++i) {
3531 QCOMPARE(textEdit->text(), expectedString[i]);
3532 QVERIFY(textEdit->canUndo());
3536 // STEP 3: Verify that we have undone everything
3537 QVERIFY(textEdit->text().isEmpty());
3538 QVERIFY(!textEdit->canUndo());
3539 QCOMPARE(spy.count(), 2);
3542 void tst_qquicktextedit::redo_data()
3544 QTest::addColumn<QStringList>("insertString");
3545 QTest::addColumn<IntList>("insertIndex");
3546 QTest::addColumn<QStringList>("expectedString");
3549 IntList insertIndex;
3550 QStringList insertString;
3551 QStringList expectedString;
3554 insertString << "World"; // World
3556 insertString << "Hello"; // HelloWorld
3558 insertString << "Well"; // WellHelloWorld
3560 insertString << "There"; // WellHelloThereWorld;
3562 expectedString << "World";
3563 expectedString << "HelloWorld";
3564 expectedString << "WellHelloWorld";
3565 expectedString << "WellHelloThereWorld";
3567 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
3571 void tst_qquicktextedit::redo()
3573 QFETCH(QStringList, insertString);
3574 QFETCH(IntList, insertIndex);
3575 QFETCH(QStringList, expectedString);
3577 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3578 QQmlComponent textEditComponent(&engine);
3579 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3580 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3581 QVERIFY(textEdit != 0);
3583 QQuickCanvas canvas;
3584 textEdit->setParentItem(canvas.rootItem());
3586 canvas.requestActivateWindow();
3587 QTest::qWaitForWindowShown(&canvas);
3588 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3590 QVERIFY(!textEdit->canUndo());
3591 QVERIFY(!textEdit->canRedo());
3593 QSignalSpy spy(textEdit, SIGNAL(canRedoChanged()));
3596 // inserts the diff strings at diff positions
3597 for (i = 0; i < insertString.size(); ++i) {
3598 if (insertIndex[i] > -1)
3599 textEdit->setCursorPosition(insertIndex[i]);
3600 for (int j = 0; j < insertString.at(i).length(); j++)
3601 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3602 QVERIFY(textEdit->canUndo());
3603 QVERIFY(!textEdit->canRedo());
3606 QCOMPARE(spy.count(), 0);
3609 while (!textEdit->text().isEmpty()) {
3610 QVERIFY(textEdit->canUndo());
3612 QVERIFY(textEdit->canRedo());
3615 QCOMPARE(spy.count(), 1);
3617 for (i = 0; i < expectedString.size(); ++i) {
3618 QVERIFY(textEdit->canRedo());
3620 QCOMPARE(textEdit->text() , expectedString[i]);
3621 QVERIFY(textEdit->canUndo());
3623 QVERIFY(!textEdit->canRedo());
3624 QCOMPARE(spy.count(), 2);
3627 void tst_qquicktextedit::undo_keypressevents_data()
3629 QTest::addColumn<KeyList>("keys");
3630 QTest::addColumn<QStringList>("expectedString");
3634 QStringList expectedString;
3647 expectedString << "BEVERYAFRAID!";
3648 expectedString << "BEVERYAFRAID";
3649 expectedString << "VERYAFRAID";
3650 expectedString << "AFRAID";
3652 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
3655 QStringList expectedString;
3658 keys << "1234" << Qt::Key_Home
3660 << Qt::Key_Right << Qt::Key_Right
3662 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3666 expectedString << "12";
3667 expectedString << "1234";
3669 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
3672 QStringList expectedString;
3678 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3680 << QKeySequence::Undo
3681 // ### Text is selected in text input
3683 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3686 expectedString << "AB";
3687 expectedString << "AB12";
3689 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
3692 QStringList expectedString;
3697 << Qt::Key_Left << Qt::Key_Left
3701 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3702 // overwriting '1234' with '5'
3704 // undoing deletion of 'AB'
3705 << QKeySequence::Undo
3706 // ### Text is selected in text input
3707 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3708 // overwriting '1234' with '6'
3711 expectedString << "ab6cd";
3712 // for versions previous to 3.2 we overwrite needed two undo operations
3713 expectedString << "ab1234cd";
3714 expectedString << "abcd";
3716 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
3719 QStringList expectedString;
3724 << Qt::Key_Backspace;
3726 expectedString << "AB";
3727 expectedString << "ABC";
3729 QTest::newRow("Inserts,backspace") << keys << expectedString;
3732 QStringList expectedString;
3736 << Qt::Key_Backspace
3740 expectedString << "ABZ";
3741 expectedString << "AB";
3742 expectedString << "ABC";
3744 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
3747 QStringList expectedString;
3750 keys << "123" << Qt::Key_Home
3752 << (Qt::Key_End | Qt::ShiftModifier)
3753 // overwriting '123' with 'ABC'
3756 expectedString << "ABC";
3757 // ### One operation in TextInput.
3758 expectedString << "A";
3759 expectedString << "123";
3761 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
3765 void tst_qquicktextedit::undo_keypressevents()
3767 QFETCH(KeyList, keys);
3768 QFETCH(QStringList, expectedString);
3770 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3771 QQmlComponent textEditComponent(&engine);
3772 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3773 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3774 QVERIFY(textEdit != 0);
3776 QQuickCanvas canvas;
3777 textEdit->setParentItem(canvas.rootItem());
3779 canvas.requestActivateWindow();
3780 QTest::qWaitForWindowShown(&canvas);
3781 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3783 simulateKeys(&canvas, keys);
3785 for (int i = 0; i < expectedString.size(); ++i) {
3786 QCOMPARE(textEdit->text() , expectedString[i]);
3789 QVERIFY(textEdit->text().isEmpty());
3792 void tst_qquicktextedit::baseUrl()
3794 QUrl localUrl("file:///tests/text.qml");
3795 QUrl remoteUrl("http://qt.nokia.com/test.qml");
3797 QQmlComponent textComponent(&engine);
3798 textComponent.setData("import QtQuick 2.0\n TextEdit {}", localUrl);
3799 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(textComponent.create());
3801 QCOMPARE(textObject->baseUrl(), localUrl);
3803 QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
3805 textObject->setBaseUrl(localUrl);
3806 QCOMPARE(textObject->baseUrl(), localUrl);
3807 QCOMPARE(spy.count(), 0);
3809 textObject->setBaseUrl(remoteUrl);
3810 QCOMPARE(textObject->baseUrl(), remoteUrl);
3811 QCOMPARE(spy.count(), 1);
3813 textObject->resetBaseUrl();
3814 QCOMPARE(textObject->baseUrl(), localUrl);
3815 QCOMPARE(spy.count(), 2);
3818 void tst_qquicktextedit::embeddedImages_data()
3820 QTest::addColumn<QUrl>("qmlfile");
3821 QTest::addColumn<QString>("error");
3822 QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
3823 QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
3824 << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
3825 QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
3826 QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
3827 QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
3828 << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
3829 QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
3832 void tst_qquicktextedit::embeddedImages()
3834 QFETCH(QUrl, qmlfile);
3835 QFETCH(QString, error);
3837 TestHTTPServer server(42332);
3838 server.serveDirectory(testFile("http"));
3840 if (!error.isEmpty())
3841 QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
3843 QQmlComponent textComponent(&engine, qmlfile);
3844 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
3846 QVERIFY(textObject != 0);
3847 QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
3849 QPixmap pm(testFile("http/exists.png"));
3850 if (error.isEmpty()) {
3851 QCOMPARE(textObject->width(), double(pm.width()));
3852 QCOMPARE(textObject->height(), double(pm.height()));
3854 QVERIFY(16 != pm.width()); // check test is effective
3855 QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
3856 QCOMPARE(textObject->height(), 16.0);
3862 void tst_qquicktextedit::emptytags_QTBUG_22058()
3864 QQuickView canvas(testFileUrl("qtbug-22058.qml"));
3865 QVERIFY(canvas.rootObject() != 0);
3868 canvas.requestActivateWindow();
3869 QTest::qWaitForWindowShown(&canvas);
3870 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("inputField")));
3871 QVERIFY(input->hasActiveFocus());
3873 QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
3874 event.setCommitString("<b>Bold<");
3875 QGuiApplication::sendEvent(input, &event);
3876 QCOMPARE(input->text(), QString("<b>Bold<"));
3877 event.setCommitString(">");
3878 QGuiApplication::sendEvent(input, &event);
3879 QCOMPARE(input->text(), QString("<b>Bold<>"));
3882 QTEST_MAIN(tst_qquicktextedit)
3884 #include "tst_qquicktextedit.moc"