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 <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativecontext.h>
49 #include <QtDeclarative/qdeclarativeexpression.h>
50 #include <QtDeclarative/qdeclarativecomponent.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 <QInputPanel>
62 #include <private/qquicktextcontrol_p.h>
63 #include "../../shared/util.h"
64 #include "../../shared/platforminputcontext.h"
65 #include <private/qinputpanel_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 = QDeclarativeDataTest::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 QDeclarativeDataTest
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();
138 void cursorVisible();
139 void delegateLoading_data();
140 void delegateLoading();
145 void canPasteEmpty();
147 void inputPanelUpdate();
148 void openInputPanel();
149 void geometrySignals();
150 void pastingRichText_QTBUG_14003();
151 void implicitSize_data();
153 void testQtQuick11Attributes();
154 void testQtQuick11Attributes_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;
202 QDeclarativeEngine engine;
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 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
310 inputPanelPrivate->testContext = 0;
313 void tst_qquicktextedit::text()
316 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QDeclarativeComponent 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 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
635 inputPanelPrivate->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->inputPanel()->inputItem(), &ev); }
726 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
727 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &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->inputPanel()->inputItem(), &ev); }
734 // empty text with implicit alignment follows the system locale-based
735 // keyboard input direction from qApp->inputPanel()->inputDirection
736 textEdit->setText("");
737 platformInputContext.setInputDirection(Qt::LeftToRight);
738 QVERIFY(qApp->inputPanel()->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->inputPanel()->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 QDeclarativeComponent 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 QDeclarativeComponent 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));
807 void tst_qquicktextedit::font()
809 //test size, then bold, then italic, then family
811 QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
812 QDeclarativeComponent texteditComponent(&engine);
813 texteditComponent.setData(componentStr.toLatin1(), QUrl());
814 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
816 QVERIFY(textEditObject != 0);
817 QCOMPARE(textEditObject->font().pointSize(), 40);
818 QCOMPARE(textEditObject->font().bold(), false);
819 QCOMPARE(textEditObject->font().italic(), false);
823 QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
824 QDeclarativeComponent texteditComponent(&engine);
825 texteditComponent.setData(componentStr.toLatin1(), QUrl());
826 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
828 QVERIFY(textEditObject != 0);
829 QCOMPARE(textEditObject->font().bold(), true);
830 QCOMPARE(textEditObject->font().italic(), false);
834 QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
835 QDeclarativeComponent texteditComponent(&engine);
836 texteditComponent.setData(componentStr.toLatin1(), QUrl());
837 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
839 QVERIFY(textEditObject != 0);
840 QCOMPARE(textEditObject->font().italic(), true);
841 QCOMPARE(textEditObject->font().bold(), false);
845 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
846 QDeclarativeComponent texteditComponent(&engine);
847 texteditComponent.setData(componentStr.toLatin1(), QUrl());
848 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
850 QVERIFY(textEditObject != 0);
851 QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
852 QCOMPARE(textEditObject->font().bold(), false);
853 QCOMPARE(textEditObject->font().italic(), false);
857 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
858 QDeclarativeComponent texteditComponent(&engine);
859 texteditComponent.setData(componentStr.toLatin1(), QUrl());
860 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
862 QVERIFY(textEditObject != 0);
863 QCOMPARE(textEditObject->font().family(), QString(""));
867 void tst_qquicktextedit::color()
871 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
872 QDeclarativeComponent texteditComponent(&engine);
873 texteditComponent.setData(componentStr.toLatin1(), QUrl());
874 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
876 QQuickTextEditPrivate *textEditPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(textEditObject));
878 QVERIFY(textEditObject);
879 QVERIFY(textEditPrivate);
880 QVERIFY(textEditPrivate->control);
882 QPalette pal = textEditPrivate->control->palette();
883 QCOMPARE(textEditPrivate->color, QColor("black"));
884 QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text));
887 for (int i = 0; i < colorStrings.size(); i++)
889 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
890 QDeclarativeComponent texteditComponent(&engine);
891 texteditComponent.setData(componentStr.toLatin1(), QUrl());
892 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
893 //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
894 QVERIFY(textEditObject != 0);
895 QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
899 for (int i = 0; i < colorStrings.size(); i++)
901 QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
902 QDeclarativeComponent texteditComponent(&engine);
903 texteditComponent.setData(componentStr.toLatin1(), QUrl());
904 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
905 QVERIFY(textEditObject != 0);
906 QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
910 for (int i = 0; i < colorStrings.size(); i++)
912 QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
913 QDeclarativeComponent texteditComponent(&engine);
914 texteditComponent.setData(componentStr.toLatin1(), QUrl());
915 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
916 QVERIFY(textEditObject != 0);
917 QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
921 QString colorStr = "#AA001234";
922 QColor testColor("#001234");
923 testColor.setAlpha(170);
925 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
926 QDeclarativeComponent texteditComponent(&engine);
927 texteditComponent.setData(componentStr.toLatin1(), QUrl());
928 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
930 QVERIFY(textEditObject != 0);
931 QCOMPARE(textEditObject->color(), testColor);
935 void tst_qquicktextedit::textMargin()
937 for (qreal i=0; i<=10; i+=0.3) {
938 QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
939 QDeclarativeComponent texteditComponent(&engine);
940 texteditComponent.setData(componentStr.toLatin1(), QUrl());
941 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
942 QVERIFY(textEditObject != 0);
943 QCOMPARE(textEditObject->textMargin(), i);
947 void tst_qquicktextedit::persistentSelection()
950 QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: true; text: \"Hello World\" }";
951 QDeclarativeComponent texteditComponent(&engine);
952 texteditComponent.setData(componentStr.toLatin1(), QUrl());
953 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
954 QVERIFY(textEditObject != 0);
955 QCOMPARE(textEditObject->persistentSelection(), true);
959 QString componentStr = "import QtQuick 2.0\nTextEdit { persistentSelection: false; text: \"Hello World\" }";
960 QDeclarativeComponent texteditComponent(&engine);
961 texteditComponent.setData(componentStr.toLatin1(), QUrl());
962 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
963 QVERIFY(textEditObject != 0);
964 QCOMPARE(textEditObject->persistentSelection(), false);
968 void tst_qquicktextedit::focusOnPress()
971 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: true; text: \"Hello World\" }";
972 QDeclarativeComponent texteditComponent(&engine);
973 texteditComponent.setData(componentStr.toLatin1(), QUrl());
974 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
975 QVERIFY(textEditObject != 0);
976 QCOMPARE(textEditObject->focusOnPress(), true);
980 QString componentStr = "import QtQuick 2.0\nTextEdit { activeFocusOnPress: false; text: \"Hello World\" }";
981 QDeclarativeComponent texteditComponent(&engine);
982 texteditComponent.setData(componentStr.toLatin1(), QUrl());
983 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
984 QVERIFY(textEditObject != 0);
985 QCOMPARE(textEditObject->focusOnPress(), false);
989 void tst_qquicktextedit::selection()
991 QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
992 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
993 QDeclarativeComponent texteditComponent(&engine);
994 texteditComponent.setData(componentStr.toLatin1(), QUrl());
995 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
996 QVERIFY(textEditObject != 0);
999 //Test selection follows cursor
1000 for (int i=0; i<= testStr.size(); i++) {
1001 textEditObject->setCursorPosition(i);
1002 QCOMPARE(textEditObject->cursorPosition(), i);
1003 QCOMPARE(textEditObject->selectionStart(), i);
1004 QCOMPARE(textEditObject->selectionEnd(), i);
1005 QVERIFY(textEditObject->selectedText().isNull());
1008 textEditObject->setCursorPosition(0);
1009 QVERIFY(textEditObject->cursorPosition() == 0);
1010 QVERIFY(textEditObject->selectionStart() == 0);
1011 QVERIFY(textEditObject->selectionEnd() == 0);
1012 QVERIFY(textEditObject->selectedText().isNull());
1014 // Verify invalid positions are ignored.
1015 textEditObject->setCursorPosition(-1);
1016 QVERIFY(textEditObject->cursorPosition() == 0);
1017 QVERIFY(textEditObject->selectionStart() == 0);
1018 QVERIFY(textEditObject->selectionEnd() == 0);
1019 QVERIFY(textEditObject->selectedText().isNull());
1021 textEditObject->setCursorPosition(textEditObject->text().count()+1);
1022 QVERIFY(textEditObject->cursorPosition() == 0);
1023 QVERIFY(textEditObject->selectionStart() == 0);
1024 QVERIFY(textEditObject->selectionEnd() == 0);
1025 QVERIFY(textEditObject->selectedText().isNull());
1028 for (int i=0; i<= testStr.size(); i++) {
1029 textEditObject->select(0,i);
1030 QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
1032 for (int i=0; i<= testStr.size(); i++) {
1033 textEditObject->select(i,testStr.size());
1034 QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
1037 textEditObject->setCursorPosition(0);
1038 QVERIFY(textEditObject->cursorPosition() == 0);
1039 QVERIFY(textEditObject->selectionStart() == 0);
1040 QVERIFY(textEditObject->selectionEnd() == 0);
1041 QVERIFY(textEditObject->selectedText().isNull());
1043 //Test Error Ignoring behaviour
1044 textEditObject->setCursorPosition(0);
1045 QVERIFY(textEditObject->selectedText().isNull());
1046 textEditObject->select(-10,0);
1047 QVERIFY(textEditObject->selectedText().isNull());
1048 textEditObject->select(100,101);
1049 QVERIFY(textEditObject->selectedText().isNull());
1050 textEditObject->select(0,-10);
1051 QVERIFY(textEditObject->selectedText().isNull());
1052 textEditObject->select(0,100);
1053 QVERIFY(textEditObject->selectedText().isNull());
1054 textEditObject->select(0,10);
1055 QVERIFY(textEditObject->selectedText().size() == 10);
1056 textEditObject->select(-10,0);
1057 QVERIFY(textEditObject->selectedText().size() == 10);
1058 textEditObject->select(100,101);
1059 QVERIFY(textEditObject->selectedText().size() == 10);
1060 textEditObject->select(0,-10);
1061 QVERIFY(textEditObject->selectedText().size() == 10);
1062 textEditObject->select(0,100);
1063 QVERIFY(textEditObject->selectedText().size() == 10);
1065 textEditObject->deselect();
1066 QVERIFY(textEditObject->selectedText().isNull());
1067 textEditObject->select(0,10);
1068 QVERIFY(textEditObject->selectedText().size() == 10);
1069 textEditObject->deselect();
1070 QVERIFY(textEditObject->selectedText().isNull());
1073 void tst_qquicktextedit::isRightToLeft_data()
1075 QTest::addColumn<QString>("text");
1076 QTest::addColumn<bool>("emptyString");
1077 QTest::addColumn<bool>("firstCharacter");
1078 QTest::addColumn<bool>("lastCharacter");
1079 QTest::addColumn<bool>("middleCharacter");
1080 QTest::addColumn<bool>("startString");
1081 QTest::addColumn<bool>("midString");
1082 QTest::addColumn<bool>("endString");
1084 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1085 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
1086 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
1087 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
1088 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
1089 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;
1090 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
1093 void tst_qquicktextedit::isRightToLeft()
1095 QFETCH(QString, text);
1096 QFETCH(bool, emptyString);
1097 QFETCH(bool, firstCharacter);
1098 QFETCH(bool, lastCharacter);
1099 QFETCH(bool, middleCharacter);
1100 QFETCH(bool, startString);
1101 QFETCH(bool, midString);
1102 QFETCH(bool, endString);
1104 QQuickTextEdit textEdit;
1105 textEdit.setText(text);
1107 // first test that the right string is delivered to the QString::isRightToLeft()
1108 QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
1109 QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
1110 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
1111 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
1112 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
1113 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
1115 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1116 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
1118 // then test that the feature actually works
1119 QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
1120 QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
1121 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
1122 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
1123 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
1124 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
1126 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1127 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
1130 void tst_qquicktextedit::keySelection()
1132 QQuickView canvas(testFileUrl("navigation.qml"));
1134 canvas.requestActivateWindow();
1135 QTest::qWaitForWindowShown(&canvas);
1136 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1137 canvas.requestActivateWindow();
1139 QVERIFY(canvas.rootObject() != 0);
1141 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1143 QVERIFY(input != 0);
1144 QTRY_VERIFY(input->hasActiveFocus() == true);
1146 QSignalSpy spy(input, SIGNAL(selectionChanged()));
1148 simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
1149 QVERIFY(input->hasActiveFocus() == true);
1150 QCOMPARE(input->selectedText(), QString("a"));
1151 QCOMPARE(spy.count(), 1);
1152 simulateKey(&canvas, Qt::Key_Right);
1153 QVERIFY(input->hasActiveFocus() == true);
1154 QCOMPARE(input->selectedText(), QString());
1155 QCOMPARE(spy.count(), 2);
1156 simulateKey(&canvas, Qt::Key_Right);
1157 QVERIFY(input->hasActiveFocus() == false);
1158 QCOMPARE(input->selectedText(), QString());
1159 QCOMPARE(spy.count(), 2);
1161 simulateKey(&canvas, Qt::Key_Left);
1162 QVERIFY(input->hasActiveFocus() == true);
1163 QCOMPARE(spy.count(), 2);
1164 simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1165 QVERIFY(input->hasActiveFocus() == true);
1166 QCOMPARE(input->selectedText(), QString("a"));
1167 QCOMPARE(spy.count(), 3);
1168 simulateKey(&canvas, Qt::Key_Left);
1169 QVERIFY(input->hasActiveFocus() == true);
1170 QCOMPARE(input->selectedText(), QString());
1171 QCOMPARE(spy.count(), 4);
1172 simulateKey(&canvas, Qt::Key_Left);
1173 QVERIFY(input->hasActiveFocus() == false);
1174 QCOMPARE(input->selectedText(), QString());
1175 QCOMPARE(spy.count(), 4);
1178 void tst_qquicktextedit::moveCursorSelection_data()
1180 QTest::addColumn<QString>("testStr");
1181 QTest::addColumn<int>("cursorPosition");
1182 QTest::addColumn<int>("movePosition");
1183 QTest::addColumn<QQuickTextEdit::SelectionMode>("mode");
1184 QTest::addColumn<int>("selectionStart");
1185 QTest::addColumn<int>("selectionEnd");
1186 QTest::addColumn<bool>("reversible");
1188 QTest::newRow("(t)he|characters")
1189 << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true;
1190 QTest::newRow("do(g)|characters")
1191 << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true;
1192 QTest::newRow("jum(p)ed|characters")
1193 << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true;
1194 QTest::newRow("jumped( )over|characters")
1195 << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true;
1196 QTest::newRow("(the )|characters")
1197 << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true;
1198 QTest::newRow("( dog)|characters")
1199 << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true;
1200 QTest::newRow("( jumped )|characters")
1201 << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true;
1202 QTest::newRow("th(e qu)ick|characters")
1203 << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true;
1204 QTest::newRow("la(zy d)og|characters")
1205 << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true;
1206 QTest::newRow("jum(ped ov)er|characters")
1207 << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true;
1208 QTest::newRow("()the|characters")
1209 << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true;
1210 QTest::newRow("dog()|characters")
1211 << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true;
1212 QTest::newRow("jum()ped|characters")
1213 << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true;
1215 QTest::newRow("<(t)he>|words")
1216 << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true;
1217 QTest::newRow("<do(g)>|words")
1218 << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true;
1219 QTest::newRow("<jum(p)ed>|words")
1220 << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true;
1221 QTest::newRow("<jumped( )>over|words")
1222 << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false;
1223 QTest::newRow("jumped<( )over>|words,reversed")
1224 << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false;
1225 QTest::newRow("<(the )>quick|words")
1226 << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false;
1227 QTest::newRow("<(the )quick>|words,reversed")
1228 << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false;
1229 QTest::newRow("<lazy( dog)>|words")
1230 << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false;
1231 QTest::newRow("lazy<( dog)>|words,reversed")
1232 << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false;
1233 QTest::newRow("<fox( jumped )>over|words")
1234 << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false;
1235 QTest::newRow("fox<( jumped )over>|words,reversed")
1236 << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false;
1237 QTest::newRow("<th(e qu)ick>|words")
1238 << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true;
1239 QTest::newRow("<la(zy d)og|words>")
1240 << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true;
1241 QTest::newRow("<jum(ped ov)er>|words")
1242 << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true;
1243 QTest::newRow("<()>the|words")
1244 << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1245 QTest::newRow("dog<()>|words")
1246 << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true;
1247 QTest::newRow("jum<()>ped|words")
1248 << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true;
1250 QTest::newRow("Hello<(,)> |words")
1251 << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true;
1252 QTest::newRow("Hello<(, )>world|words")
1253 << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1254 QTest::newRow("Hello<(, )world>|words,reversed")
1255 << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1256 QTest::newRow("<Hel(lo, )>world|words")
1257 << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1258 QTest::newRow("<Hel(lo, )world>|words,reversed")
1259 << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false;
1260 QTest::newRow("<Hel(lo)>,|words")
1261 << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true;
1262 QTest::newRow("Hello<()>,|words")
1263 << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true;
1264 QTest::newRow("Hello,<()>|words")
1265 << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true;
1266 QTest::newRow("Hello<,( )>world|words")
1267 << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1268 QTest::newRow("Hello,<( )world>|words,reversed")
1269 << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1270 QTest::newRow("Hello<,( world)>|words")
1271 << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1272 QTest::newRow("Hello,<( world)>|words,reversed")
1273 << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1274 QTest::newRow("Hello<,( world!)>|words")
1275 << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false;
1276 QTest::newRow("Hello,<( world!)>|words,reversed")
1277 << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1278 QTest::newRow("Hello<(, world!)>|words")
1279 << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true;
1280 QTest::newRow("world<(!)>|words")
1281 << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true;
1282 QTest::newRow("world!<()>)|words")
1283 << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true;
1284 QTest::newRow("world<()>!)|words")
1285 << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true;
1287 QTest::newRow("<(,)>olleH |words")
1288 << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true;
1289 QTest::newRow("<dlrow( ,)>olleH|words")
1290 << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1291 QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1292 << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1293 QTest::newRow("<dlrow( ,ol)leH>|words")
1294 << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false;
1295 QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1296 << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1297 QTest::newRow(",<(ol)leH>,|words")
1298 << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true;
1299 QTest::newRow(",<()>olleH|words")
1300 << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true;
1301 QTest::newRow("<()>,olleH|words")
1302 << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true;
1303 QTest::newRow("<dlrow( )>,olleH|words")
1304 << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1305 QTest::newRow("dlrow<( ),>olleH|words,reversed")
1306 << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1307 QTest::newRow("<(dlrow )>,olleH|words")
1308 << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1309 QTest::newRow("<(dlrow ),>olleH|words,reversed")
1310 << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1311 QTest::newRow("<(!dlrow )>,olleH|words")
1312 << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1313 QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1314 << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false;
1315 QTest::newRow("(!dlrow ,)olleH|words")
1316 << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true;
1317 QTest::newRow("<(!)>dlrow|words")
1318 << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true;
1319 QTest::newRow("<()>!dlrow|words")
1320 << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1321 QTest::newRow("!<()>dlrow|words")
1322 << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true;
1325 void tst_qquicktextedit::moveCursorSelection()
1327 QFETCH(QString, testStr);
1328 QFETCH(int, cursorPosition);
1329 QFETCH(int, movePosition);
1330 QFETCH(QQuickTextEdit::SelectionMode, mode);
1331 QFETCH(int, selectionStart);
1332 QFETCH(int, selectionEnd);
1333 QFETCH(bool, reversible);
1335 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1336 QDeclarativeComponent textinputComponent(&engine);
1337 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1338 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(textinputComponent.create());
1339 QVERIFY(texteditObject != 0);
1341 texteditObject->setCursorPosition(cursorPosition);
1342 texteditObject->moveCursorSelection(movePosition, mode);
1344 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1345 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1346 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1349 texteditObject->setCursorPosition(movePosition);
1350 texteditObject->moveCursorSelection(cursorPosition, mode);
1352 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1353 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1354 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1358 void tst_qquicktextedit::moveCursorSelectionSequence_data()
1360 QTest::addColumn<QString>("testStr");
1361 QTest::addColumn<int>("cursorPosition");
1362 QTest::addColumn<int>("movePosition1");
1363 QTest::addColumn<int>("movePosition2");
1364 QTest::addColumn<int>("selection1Start");
1365 QTest::addColumn<int>("selection1End");
1366 QTest::addColumn<int>("selection2Start");
1367 QTest::addColumn<int>("selection2End");
1369 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1374 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1379 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1384 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1389 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1394 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1399 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1404 QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1409 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1414 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1419 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1424 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1429 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1434 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1439 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1444 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1449 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1454 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1460 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1465 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1471 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1476 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1483 void tst_qquicktextedit::moveCursorSelectionSequence()
1485 QFETCH(QString, testStr);
1486 QFETCH(int, cursorPosition);
1487 QFETCH(int, movePosition1);
1488 QFETCH(int, movePosition2);
1489 QFETCH(int, selection1Start);
1490 QFETCH(int, selection1End);
1491 QFETCH(int, selection2Start);
1492 QFETCH(int, selection2End);
1494 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1495 QDeclarativeComponent texteditComponent(&engine);
1496 texteditComponent.setData(componentStr.toLatin1(), QUrl());
1497 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1498 QVERIFY(texteditObject != 0);
1500 texteditObject->setCursorPosition(cursorPosition);
1502 texteditObject->moveCursorSelection(movePosition1, QQuickTextEdit::SelectWords);
1503 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1504 QCOMPARE(texteditObject->selectionStart(), selection1Start);
1505 QCOMPARE(texteditObject->selectionEnd(), selection1End);
1507 texteditObject->moveCursorSelection(movePosition2, QQuickTextEdit::SelectWords);
1508 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1509 QCOMPARE(texteditObject->selectionStart(), selection2Start);
1510 QCOMPARE(texteditObject->selectionEnd(), selection2End);
1514 void tst_qquicktextedit::mouseSelection_data()
1516 QTest::addColumn<QString>("qmlfile");
1517 QTest::addColumn<int>("from");
1518 QTest::addColumn<int>("to");
1519 QTest::addColumn<QString>("selectedText");
1522 QTest::newRow("on") << testFile("mouseselection_true.qml") << 4 << 9 << "45678";
1523 QTest::newRow("off") << testFile("mouseselection_false.qml") << 4 << 9 << QString();
1524 QTest::newRow("default") << testFile("mouseselection_default.qml") << 4 << 9 << QString();
1525 QTest::newRow("off word selection") << testFile("mouseselection_false_words.qml") << 4 << 9 << QString();
1526 QTest::newRow("on word selection (4,9)") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789";
1527 QTest::newRow("on word selection (2,13)") << testFile("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1528 QTest::newRow("on word selection (2,30)") << testFile("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1529 QTest::newRow("on word selection (9,13)") << testFile("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1530 QTest::newRow("on word selection (9,30)") << testFile("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1531 QTest::newRow("on word selection (13,2)") << testFile("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1532 QTest::newRow("on word selection (20,2)") << testFile("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1533 QTest::newRow("on word selection (12,9)") << testFile("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1534 QTest::newRow("on word selection (30,9)") << testFile("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1537 void tst_qquicktextedit::mouseSelection()
1539 QFETCH(QString, qmlfile);
1542 QFETCH(QString, selectedText);
1544 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1547 canvas.requestActivateWindow();
1548 QTest::qWaitForWindowShown(&canvas);
1549 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1551 QVERIFY(canvas.rootObject() != 0);
1552 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1553 QVERIFY(textEditObject != 0);
1555 // press-and-drag-and-release from x1 to x2
1556 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1557 QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1558 QTest::mousePress(&canvas, Qt::LeftButton, 0, p1);
1559 QTest::mouseMove(&canvas, p2);
1560 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2);
1562 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1564 // Clicking and shift to clicking between the same points should select the same text.
1565 textEditObject->setCursorPosition(0);
1566 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1567 QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1569 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1572 void tst_qquicktextedit::dragMouseSelection()
1574 QString qmlfile = testFile("mouseselection_true.qml");
1576 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1579 canvas.requestActivateWindow();
1580 QTest::qWaitForWindowShown(&canvas);
1581 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1583 QVERIFY(canvas.rootObject() != 0);
1584 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1585 QVERIFY(textEditObject != 0);
1587 // press-and-drag-and-release from x1 to x2
1590 int y = textEditObject->height()/2;
1591 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1592 QTest::mouseMove(&canvas, QPoint(x2, y));
1593 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1596 QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1598 // press and drag the current selection.
1601 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1602 QTest::mouseMove(&canvas, QPoint(x2, y));
1603 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1606 QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1608 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1611 void tst_qquicktextedit::mouseSelectionMode_data()
1613 QTest::addColumn<QString>("qmlfile");
1614 QTest::addColumn<bool>("selectWords");
1617 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1618 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1619 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1622 void tst_qquicktextedit::mouseSelectionMode()
1624 QFETCH(QString, qmlfile);
1625 QFETCH(bool, selectWords);
1627 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1629 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1632 canvas.requestActivateWindow();
1633 QTest::qWaitForWindowShown(&canvas);
1634 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1636 QVERIFY(canvas.rootObject() != 0);
1637 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1638 QVERIFY(textEditObject != 0);
1640 // press-and-drag-and-release from x1 to x2
1643 int y = textEditObject->height()/2;
1644 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1645 QTest::mouseMove(&canvas, QPoint(x2, y));
1646 //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
1647 // QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1648 // QGuiApplication::sendEvent(&canvas, &mv);
1649 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1650 QString str = textEditObject->selectedText();
1652 QTRY_COMPARE(textEditObject->selectedText(), text);
1654 QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1655 QVERIFY(str != text);
1659 void tst_qquicktextedit::inputMethodHints()
1661 QQuickView canvas(testFileUrl("inputmethodhints.qml"));
1663 canvas.requestActivateWindow();
1665 QVERIFY(canvas.rootObject() != 0);
1666 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1667 QVERIFY(textEditObject != 0);
1668 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1669 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1670 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1673 void tst_qquicktextedit::positionAt()
1675 QQuickView canvas(testFileUrl("positionAt.qml"));
1676 QVERIFY(canvas.rootObject() != 0);
1678 canvas.requestActivateWindow();
1679 canvas.requestActivateWindow();
1680 QTest::qWaitForWindowShown(&canvas);
1682 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1683 QVERIFY(texteditObject != 0);
1685 QTextLayout layout(texteditObject->text());
1686 layout.setFont(texteditObject->font());
1688 if (!qmlDisableDistanceField()) {
1690 option.setUseDesignMetrics(true);
1691 layout.setTextOption(option);
1694 layout.beginLayout();
1695 QTextLine line = layout.createLine();
1698 const int y0 = line.height() / 2;
1699 const int y1 = line.height() * 3 / 2;
1701 int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
1703 int widthBegin = floor(line.cursorToX(pos - 1));
1704 int widthEnd = ceil(line.cursorToX(pos + 1));
1706 QVERIFY(widthBegin <= texteditObject->width() / 2);
1707 QVERIFY(widthEnd >= texteditObject->width() / 2);
1709 const qreal x0 = texteditObject->positionToRectangle(pos).x();
1710 const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
1712 QString preeditText = texteditObject->text().mid(0, pos);
1713 texteditObject->setText(texteditObject->text().mid(pos));
1714 texteditObject->setCursorPosition(0);
1716 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1717 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent);
1719 // Check all points within the preedit text return the same position.
1720 QCOMPARE(texteditObject->positionAt(0, y0), 0);
1721 QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
1722 QCOMPARE(texteditObject->positionAt(x0, y0), 0);
1724 // Verify positioning returns to normal after the preedit text.
1725 QCOMPARE(texteditObject->positionAt(x1, y0), 1);
1726 QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
1728 QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
1731 void tst_qquicktextedit::linkActivated()
1733 QQuickView canvas(testFileUrl("linkActivated.qml"));
1734 QVERIFY(canvas.rootObject() != 0);
1736 canvas.requestActivateWindow();
1737 QTest::qWaitForWindowShown(&canvas);
1739 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1740 QVERIFY(texteditObject != 0);
1742 QSignalSpy spy(texteditObject, SIGNAL(linkActivated(QString)));
1744 const QString link("http://example.com/");
1746 const QPointF linkPos = texteditObject->positionToRectangle(7).center();
1747 const QPointF textPos = texteditObject->positionToRectangle(2).center();
1749 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
1750 QTRY_COMPARE(spy.count(), 1);
1751 QCOMPARE(spy.last()[0].toString(), link);
1753 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
1755 QCOMPARE(spy.count(), 1);
1757 texteditObject->setReadOnly(true);
1759 QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
1760 QTRY_COMPARE(spy.count(), 2);
1761 QCOMPARE(spy.last()[0].toString(), link);
1763 QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
1765 QCOMPARE(spy.count(), 2);
1768 void tst_qquicktextedit::cursorDelegate()
1770 QQuickView view(testFileUrl("cursorTest.qml"));
1772 view.requestActivateWindow();
1773 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
1774 QVERIFY(textEditObject != 0);
1775 QVERIFY(textEditObject->findChild<QQuickItem*>("cursorInstance"));
1776 //Test Delegate gets created
1777 textEditObject->setFocus(true);
1778 QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>("cursorInstance");
1779 QVERIFY(delegateObject);
1780 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1781 //Test Delegate gets moved
1782 for (int i=0; i<= textEditObject->text().length(); i++) {
1783 textEditObject->setCursorPosition(i);
1784 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1785 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1787 // Clear preedit text;
1788 QInputMethodEvent event;
1789 QGuiApplication::sendEvent(&view, &event);
1792 // Test delegate gets moved on mouse press.
1793 textEditObject->setSelectByMouse(true);
1794 textEditObject->setCursorPosition(0);
1795 const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
1796 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
1798 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1799 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1800 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1802 // Test delegate gets moved on mouse drag
1803 textEditObject->setCursorPosition(0);
1804 const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
1805 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
1806 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1807 QGuiApplication::sendEvent(&view, &mv);
1808 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
1810 QTRY_COMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1811 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1813 textEditObject->setReadOnly(true);
1814 textEditObject->setCursorPosition(0);
1815 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1817 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1818 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1819 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1821 textEditObject->setCursorPosition(0);
1822 QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1824 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1825 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1826 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1828 textEditObject->setCursorPosition(0);
1829 QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1830 QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1831 //Test Delegate gets deleted
1832 textEditObject->setCursorDelegate(0);
1833 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
1836 void tst_qquicktextedit::cursorVisible()
1838 QQuickView view(testFileUrl("cursorVisible.qml"));
1840 view.requestActivateWindow();
1841 QTest::qWaitForWindowShown(&view);
1842 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1844 QQuickTextEdit edit;
1845 QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
1847 QCOMPARE(edit.isCursorVisible(), false);
1849 edit.setCursorVisible(true);
1850 QCOMPARE(edit.isCursorVisible(), true);
1851 QCOMPARE(spy.count(), 1);
1853 edit.setCursorVisible(false);
1854 QCOMPARE(edit.isCursorVisible(), false);
1855 QCOMPARE(spy.count(), 2);
1857 edit.setFocus(true);
1858 QCOMPARE(edit.isCursorVisible(), false);
1859 QCOMPARE(spy.count(), 2);
1861 edit.setParentItem(view.rootObject());
1862 QCOMPARE(edit.isCursorVisible(), true);
1863 QCOMPARE(spy.count(), 3);
1865 edit.setFocus(false);
1866 QCOMPARE(edit.isCursorVisible(), false);
1867 QCOMPARE(spy.count(), 4);
1869 edit.setFocus(true);
1870 QCOMPARE(edit.isCursorVisible(), true);
1871 QCOMPARE(spy.count(), 5);
1873 QQuickView alternateView;
1874 alternateView.show();
1875 alternateView.requestActivateWindow();
1876 QTest::qWaitForWindowShown(&alternateView);
1878 QCOMPARE(edit.isCursorVisible(), false);
1879 QCOMPARE(spy.count(), 6);
1881 view.requestActivateWindow();
1882 QTest::qWaitForWindowShown(&view);
1883 QCOMPARE(edit.isCursorVisible(), true);
1884 QCOMPARE(spy.count(), 7);
1887 void tst_qquicktextedit::delegateLoading_data()
1889 QTest::addColumn<QString>("qmlfile");
1890 QTest::addColumn<QString>("error");
1893 QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
1894 QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
1895 QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
1898 void tst_qquicktextedit::delegateLoading()
1901 QSKIP("Test crashes during canvas tear down. QTBUG-23010");
1903 QFETCH(QString, qmlfile);
1904 QFETCH(QString, error);
1906 TestHTTPServer server(42332);
1907 server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect);
1908 server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay);
1909 server.serveDirectory(testFile("http"));
1911 QQuickView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
1913 view.requestActivateWindow();
1915 if (!error.isEmpty()) {
1916 QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
1917 QTRY_VERIFY(view.status()==QQuickView::Error);
1918 QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
1920 QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
1921 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
1922 // view.rootObject()->dumpObjectTree();
1923 QVERIFY(textEditObject != 0);
1924 textEditObject->setFocus(true);
1925 QQuickItem *delegate;
1926 delegate = view.rootObject()->findChild<QQuickItem*>("delegateOkay");
1928 delegate = view.rootObject()->findChild<QQuickItem*>("delegateSlow");
1935 //A test should be added here with a component which is ready but component.create() returns null
1936 //Not sure how to accomplish this with QQuickTextEdits cursor delegate
1937 //###This was only needed for code coverage, and could be a case of overzealous defensive programming
1938 //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB");
1939 //QVERIFY(!delegate);
1943 TextEdit element should only handle left/right keys until the cursor reaches
1944 the extent of the text, then they should ignore the keys.
1946 void tst_qquicktextedit::navigation()
1948 QQuickView canvas(testFileUrl("navigation.qml"));
1950 canvas.requestActivateWindow();
1952 QVERIFY(canvas.rootObject() != 0);
1954 QQuickItem *input = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1956 QVERIFY(input != 0);
1957 QTRY_VERIFY(input->hasActiveFocus() == true);
1958 simulateKey(&canvas, Qt::Key_Left);
1959 QVERIFY(input->hasActiveFocus() == false);
1960 simulateKey(&canvas, Qt::Key_Right);
1961 QVERIFY(input->hasActiveFocus() == true);
1962 simulateKey(&canvas, Qt::Key_Right);
1963 QVERIFY(input->hasActiveFocus() == true);
1964 simulateKey(&canvas, Qt::Key_Right);
1965 QVERIFY(input->hasActiveFocus() == false);
1966 simulateKey(&canvas, Qt::Key_Left);
1967 QVERIFY(input->hasActiveFocus() == true);
1970 void tst_qquicktextedit::copyAndPaste() {
1971 #ifndef QT_NO_CLIPBOARD
1975 PasteboardRef pasteboard;
1976 OSStatus status = PasteboardCreate(0, &pasteboard);
1977 if (status == noErr)
1978 CFRelease(pasteboard);
1980 QSKIP("This machine doesn't support the clipboard");
1984 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1985 QDeclarativeComponent textEditComponent(&engine);
1986 textEditComponent.setData(componentStr.toLatin1(), QUrl());
1987 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
1988 QVERIFY(textEdit != 0);
1991 QCOMPARE(textEdit->text().length(), 12);
1992 textEdit->select(0, textEdit->text().length());;
1994 QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
1995 QCOMPARE(textEdit->selectedText().length(), 12);
1996 textEdit->setCursorPosition(0);
1997 QVERIFY(textEdit->canPaste());
1999 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2000 QCOMPARE(textEdit->text().length(), 24);
2003 QVERIFY(textEdit->canPaste());
2004 textEdit->setReadOnly(true);
2005 QVERIFY(!textEdit->canPaste());
2006 textEdit->setReadOnly(false);
2007 QVERIFY(textEdit->canPaste());
2010 // test that document and internal text attribute are in sync
2011 QQuickItemPrivate* pri = QQuickItemPrivate::get(textEdit);
2012 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri);
2013 QCOMPARE(textEdit->text(), editPrivate->text);
2016 textEdit->setCursorPosition(0);
2017 textEdit->selectWord();
2018 QCOMPARE(textEdit->selectedText(), QString("Hello"));
2020 // select all and cut
2021 textEdit->selectAll();
2023 QCOMPARE(textEdit->text().length(), 0);
2025 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2026 QCOMPARE(textEdit->text().length(), 24);
2030 void tst_qquicktextedit::canPaste() {
2031 #ifndef QT_NO_CLIPBOARD
2033 QGuiApplication::clipboard()->setText("Some text");
2035 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2036 QDeclarativeComponent textEditComponent(&engine);
2037 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2038 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2039 QVERIFY(textEdit != 0);
2041 // check initial value - QTBUG-17765
2042 QQuickTextControl tc(0);
2043 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2048 void tst_qquicktextedit::canPasteEmpty() {
2049 #ifndef QT_NO_CLIPBOARD
2051 QGuiApplication::clipboard()->clear();
2053 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2054 QDeclarativeComponent textEditComponent(&engine);
2055 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2056 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2057 QVERIFY(textEdit != 0);
2059 // check initial value - QTBUG-17765
2060 QQuickTextControl tc(0);
2061 QCOMPARE(textEdit->canPaste(), tc.canPaste());
2066 void tst_qquicktextedit::readOnly()
2068 QQuickView canvas(testFileUrl("readOnly.qml"));
2070 canvas.requestActivateWindow();
2072 QVERIFY(canvas.rootObject() != 0);
2074 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2077 QTRY_VERIFY(edit->hasActiveFocus() == true);
2078 QVERIFY(edit->isReadOnly() == true);
2079 QString initial = edit->text();
2080 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2081 simulateKey(&canvas, k);
2082 simulateKey(&canvas, Qt::Key_Return);
2083 simulateKey(&canvas, Qt::Key_Space);
2084 simulateKey(&canvas, Qt::Key_Escape);
2085 QCOMPARE(edit->text(), initial);
2087 edit->setCursorPosition(3);
2088 edit->setReadOnly(false);
2089 QCOMPARE(edit->isReadOnly(), false);
2090 QCOMPARE(edit->cursorPosition(), edit->text().length());
2093 void tst_qquicktextedit::simulateKey(QQuickView *view, int key, Qt::KeyboardModifiers modifiers)
2095 QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
2096 QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
2098 QGuiApplication::sendEvent(view, &press);
2099 QGuiApplication::sendEvent(view, &release);
2102 void tst_qquicktextedit::textInput()
2104 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2106 view.requestActivateWindow();
2107 QTest::qWaitForWindowShown(&view);
2108 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2109 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2111 QVERIFY(edit->hasActiveFocus() == true);
2113 // test that input method event is committed and change signal is emitted
2114 QSignalSpy spy(edit, SIGNAL(textChanged()));
2115 QInputMethodEvent event;
2116 event.setCommitString( "Hello world!", 0, 0);
2117 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2118 QCOMPARE(edit->text(), QString("Hello world!"));
2119 QCOMPARE(spy.count(), 1);
2122 // test that document and internal text attribute are in sync
2123 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(edit));
2124 QCOMPARE(editPrivate->text, QString("Hello world!"));
2126 // test that tentative commit is included in text property
2129 QList<QInputMethodEvent::Attribute> attributes;
2130 QInputMethodEvent event2("preedit", attributes);
2131 event2.setTentativeCommitString("string");
2132 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event2);
2133 QCOMPARE(spy.count(), 1);
2134 QCOMPARE(edit->text(), QString("string"));
2136 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
2137 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &queryEvent);
2138 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
2140 edit->setReadOnly(true);
2141 QGuiApplication::sendEvent(edit, &queryEvent);
2142 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
2145 void tst_qquicktextedit::inputPanelUpdate()
2147 PlatformInputContext platformInputContext;
2148 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2149 inputPanelPrivate->testContext = &platformInputContext;
2151 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2153 view.requestActivateWindow();
2154 QTest::qWaitForWindowShown(&view);
2155 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2156 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2158 QVERIFY(edit->hasActiveFocus() == true);
2160 // text change even without cursor position change needs to trigger update
2161 edit->setText("test");
2162 platformInputContext.clear();
2163 edit->setText("xxxx");
2164 QVERIFY(platformInputContext.m_updateCallCount > 0);
2166 // input method event replacing text
2167 platformInputContext.clear();
2169 QInputMethodEvent inputMethodEvent;
2170 inputMethodEvent.setCommitString("y", -1, 1);
2171 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2173 QVERIFY(platformInputContext.m_updateCallCount > 0);
2175 // input method changing selection
2176 platformInputContext.clear();
2178 QList<QInputMethodEvent::Attribute> attributes;
2179 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
2180 QInputMethodEvent inputMethodEvent("", attributes);
2181 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2183 QVERIFY(edit->selectionStart() != edit->selectionEnd());
2184 QVERIFY(platformInputContext.m_updateCallCount > 0);
2187 platformInputContext.clear();
2188 QFont font = edit->font();
2189 font.setBold(!font.bold());
2190 edit->setFont(font);
2191 QVERIFY(platformInputContext.m_updateCallCount > 0);
2194 platformInputContext.clear();
2196 QInputMethodEvent inputMethodEvent;
2197 inputMethodEvent.setCommitString("y");
2198 QGuiApplication::sendEvent(edit, &inputMethodEvent);
2200 QVERIFY(platformInputContext.m_updateCallCount > 0);
2202 // changing cursor position
2203 platformInputContext.clear();
2204 edit->setCursorPosition(0);
2205 QVERIFY(platformInputContext.m_updateCallCount > 0);
2207 // continuing with selection
2208 platformInputContext.clear();
2209 edit->moveCursorSelection(1);
2210 QVERIFY(platformInputContext.m_updateCallCount > 0);
2212 // read only disabled input method
2213 platformInputContext.clear();
2214 edit->setReadOnly(true);
2215 QVERIFY(platformInputContext.m_updateCallCount > 0);
2216 edit->setReadOnly(false);
2218 // no updates while no focus
2219 edit->setFocus(false);
2220 platformInputContext.clear();
2221 edit->setText("Foo");
2222 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2223 edit->setCursorPosition(1);
2224 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2226 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2227 edit->setReadOnly(true);
2228 QCOMPARE(platformInputContext.m_updateCallCount, 0);
2231 void tst_qquicktextedit::openInputPanel()
2233 PlatformInputContext platformInputContext;
2234 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2235 inputPanelPrivate->testContext = &platformInputContext;
2237 QQuickView view(testFileUrl("openInputPanel.qml"));
2239 view.requestActivateWindow();
2240 QTest::qWaitForWindowShown(&view);
2241 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2243 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2246 // check default values
2247 QVERIFY(edit->focusOnPress());
2248 QVERIFY(!edit->hasActiveFocus());
2249 qDebug() << &edit << qApp->inputPanel()->inputItem();
2250 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2252 QCOMPARE(qApp->inputPanel()->visible(), false);
2254 // input panel should open on focus
2255 QPoint centerPoint(view.width()/2, view.height()/2);
2256 Qt::KeyboardModifiers noModifiers = 0;
2257 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2258 QGuiApplication::processEvents();
2259 QVERIFY(edit->hasActiveFocus());
2260 QCOMPARE(qApp->inputPanel()->inputItem(), edit);
2261 QCOMPARE(qApp->inputPanel()->visible(), true);
2262 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2264 // input panel should be re-opened when pressing already focused TextEdit
2265 qApp->inputPanel()->hide();
2266 QCOMPARE(qApp->inputPanel()->visible(), false);
2267 QVERIFY(edit->hasActiveFocus());
2268 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2269 QGuiApplication::processEvents();
2270 QCOMPARE(qApp->inputPanel()->visible(), true);
2271 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2273 // input panel should stay visible if focus is lost to another text editor
2274 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2275 QQuickTextEdit anotherEdit;
2276 anotherEdit.setParentItem(view.rootObject());
2277 anotherEdit.setFocus(true);
2278 QCOMPARE(qApp->inputPanel()->visible(), true);
2279 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherEdit));
2280 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2282 anotherEdit.setFocus(false);
2283 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2284 QCOMPARE(view.activeFocusItem(), view.rootItem());
2285 anotherEdit.setFocus(true);
2287 // input item should be null if focus is lost to an item that doesn't accept inputs
2289 item.setParentItem(view.rootObject());
2290 item.setFocus(true);
2291 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2292 QCOMPARE(view.activeFocusItem(), &item);
2294 qApp->inputPanel()->hide();
2296 // input panel should not be opened if TextEdit is read only
2297 edit->setReadOnly(true);
2298 edit->setFocus(true);
2299 QCOMPARE(qApp->inputPanel()->visible(), false);
2300 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2301 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2302 QGuiApplication::processEvents();
2303 QCOMPARE(qApp->inputPanel()->visible(), false);
2305 // input panel should not be opened if focusOnPress is set to false
2306 edit->setFocusOnPress(false);
2307 edit->setFocus(false);
2308 edit->setFocus(true);
2309 QCOMPARE(qApp->inputPanel()->visible(), false);
2310 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2311 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2312 QCOMPARE(qApp->inputPanel()->visible(), false);
2314 // input panel should open when openSoftwareInputPanel is called
2315 edit->openSoftwareInputPanel();
2316 QCOMPARE(qApp->inputPanel()->visible(), true);
2318 // input panel should close when closeSoftwareInputPanel is called
2319 edit->closeSoftwareInputPanel();
2320 QCOMPARE(qApp->inputPanel()->visible(), false);
2322 inputPanelPrivate->testContext = 0;
2325 void tst_qquicktextedit::geometrySignals()
2327 QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2328 QObject *o = component.create();
2330 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2331 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2335 void tst_qquicktextedit::pastingRichText_QTBUG_14003()
2337 #ifndef QT_NO_CLIPBOARD
2338 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2339 QDeclarativeComponent component(&engine);
2340 component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2341 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(component.create());
2343 QTRY_VERIFY(obj != 0);
2344 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2346 QMimeData *mData = new QMimeData;
2347 mData->setHtml("<font color=\"red\">Hello</font>");
2348 QGuiApplication::clipboard()->setMimeData(mData);
2351 QTRY_VERIFY(obj->text() == "");
2352 QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2356 void tst_qquicktextedit::implicitSize_data()
2358 QTest::addColumn<QString>("text");
2359 QTest::addColumn<QString>("wrap");
2360 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap";
2361 QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap";
2362 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap";
2363 QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap";
2366 void tst_qquicktextedit::implicitSize()
2368 QFETCH(QString, text);
2369 QFETCH(QString, wrap);
2370 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
2371 QDeclarativeComponent textComponent(&engine);
2372 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2373 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
2375 QVERIFY(textObject->width() < textObject->implicitWidth());
2376 QVERIFY(textObject->height() == textObject->implicitHeight());
2378 textObject->resetWidth();
2379 QVERIFY(textObject->width() == textObject->implicitWidth());
2380 QVERIFY(textObject->height() == textObject->implicitHeight());
2383 void tst_qquicktextedit::testQtQuick11Attributes()
2385 QFETCH(QString, code);
2386 QFETCH(QString, warning);
2387 QFETCH(QString, error);
2389 QDeclarativeEngine engine;
2392 QDeclarativeComponent valid(&engine);
2393 valid.setData("import QtQuick 2.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2394 obj = valid.create();
2396 QVERIFY(valid.errorString().isEmpty());
2399 QDeclarativeComponent invalid(&engine);
2400 invalid.setData("import QtQuick 1.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2401 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2402 obj = invalid.create();
2403 QCOMPARE(invalid.errorString(), error);
2407 void tst_qquicktextedit::testQtQuick11Attributes_data()
2409 QTest::addColumn<QString>("code");
2410 QTest::addColumn<QString>("warning");
2411 QTest::addColumn<QString>("error");
2413 QTest::newRow("canPaste") << "property bool foo: canPaste"
2414 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2417 QTest::newRow("lineCount") << "property int foo: lineCount"
2418 << "<Unknown File>:1: ReferenceError: Can't find variable: lineCount"
2421 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2422 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2425 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2426 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2429 QTest::newRow("onLinkActivated") << "onLinkActivated: {}"
2430 << "QDeclarativeComponent: Component is not ready"
2431 << ":1 \"TextEdit.onLinkActivated\" is not available in QtQuick 1.0.\n";
2434 void tst_qquicktextedit::preeditCursorRectangle()
2436 QString preeditText = "super";
2438 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2440 view.requestActivateWindow();
2441 QTest::qWaitForWindowShown(&view);
2443 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2444 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2447 QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
2448 QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2452 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2453 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2454 QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2456 // Verify that the micro focus rect is positioned the same for position 0 as
2457 // it would be if there was no preedit text.
2458 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2459 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, preeditText.length(), QVariant()));
2460 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2461 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2462 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2463 QCOMPARE(currentRect, previousRect);
2464 QCOMPARE(editSpy.count(), 0);
2465 QCOMPARE(panelSpy.count(), 0);
2467 // Verify that the micro focus rect moves to the left as the cursor position
2469 for (int i = 1; i <= 5; ++i) {
2470 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
2471 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, preeditText.length(), QVariant()));
2472 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2473 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2474 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2475 QVERIFY(previousRect.left() < currentRect.left());
2476 QVERIFY(editSpy.count() > 0); editSpy.clear();
2477 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2478 previousRect = currentRect;
2481 // Verify that if there is no preedit cursor then the micro focus rect is the
2482 // same as it would be if it were positioned at the end of the preedit text.
2483 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2486 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2487 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent); }
2488 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2489 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2490 QCOMPARE(currentRect, previousRect);
2491 QVERIFY(editSpy.count() > 0);
2492 QVERIFY(panelSpy.count() > 0);
2495 void tst_qquicktextedit::inputMethodComposing()
2497 QString text = "supercalifragisiticexpialidocious!";
2499 QQuickView view(testFileUrl("inputContext.qml"));
2501 view.requestActivateWindow();
2502 QTest::qWaitForWindowShown(&view);
2503 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2504 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2506 QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
2507 edit->setCursorPosition(12);
2509 QCOMPARE(edit->isInputMethodComposing(), false);
2512 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2513 QGuiApplication::sendEvent(edit, &event);
2516 QCOMPARE(edit->isInputMethodComposing(), true);
2517 QCOMPARE(spy.count(), 1);
2520 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2521 QGuiApplication::sendEvent(edit, &event);
2523 QCOMPARE(spy.count(), 1);
2526 QInputMethodEvent event;
2527 QGuiApplication::sendEvent(edit, &event);
2529 QCOMPARE(edit->isInputMethodComposing(), false);
2530 QCOMPARE(spy.count(), 2);
2533 void tst_qquicktextedit::cursorRectangleSize()
2535 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
2536 QVERIFY(canvas->rootObject() != 0);
2537 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(canvas->rootObject());
2539 // make sure cursor rectangle is not at (0,0)
2542 textEdit->setCursorPosition(3);
2543 QVERIFY(textEdit != 0);
2544 textEdit->setFocus(true);
2546 canvas->requestActivateWindow();
2547 QTest::qWaitForWindowShown(canvas);
2549 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2550 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2551 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
2553 QRect cursorRectFromItem = textEdit->cursorRectangle();
2554 QRectF cursorRectFromPositionToRectangle = textEdit->positionToRectangle(textEdit->cursorPosition());
2556 // item and input query cursor rectangles match
2557 QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
2559 // item cursor rectangle and positionToRectangle calculations match
2560 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
2562 // item-canvas transform and input item transform match
2563 QCOMPARE(QQuickItemPrivate::get(textEdit)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
2565 // input panel cursorRectangle property and tranformed item cursor rectangle match
2566 QRectF sceneCursorRect = QQuickItemPrivate::get(textEdit)->itemToCanvasTransform().mapRect(cursorRectFromItem);
2567 QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
2572 void tst_qquicktextedit::getText_data()
2574 QTest::addColumn<QString>("text");
2575 QTest::addColumn<int>("start");
2576 QTest::addColumn<int>("end");
2577 QTest::addColumn<QString>("expectedText");
2579 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
2580 const QString plainBoldText = QStringLiteral("This is some bold text");
2582 QTest::newRow("all plain text")
2584 << 0 << standard.at(0).length()
2587 QTest::newRow("plain text sub string")
2590 << standard.at(0).mid(0, 12);
2592 QTest::newRow("plain text sub string reversed")
2595 << standard.at(0).mid(0, 12);
2597 QTest::newRow("plain text cropped beginning")
2600 << standard.at(0).mid(0, 4);
2602 QTest::newRow("plain text cropped end")
2604 << 23 << standard.at(0).length() + 8
2605 << standard.at(0).mid(23);
2607 QTest::newRow("plain text cropped beginning and end")
2609 << -9 << standard.at(0).length() + 4
2612 QTest::newRow("all rich text")
2614 << 0 << plainBoldText.length()
2617 QTest::newRow("rich text sub string")
2620 << plainBoldText.mid(14, 7);
2623 void tst_qquicktextedit::getText()
2625 QFETCH(QString, text);
2628 QFETCH(QString, expectedText);
2630 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + text + "\" }";
2631 QDeclarativeComponent textEditComponent(&engine);
2632 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2633 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2634 QVERIFY(textEdit != 0);
2636 QCOMPARE(textEdit->getText(start, end), expectedText);
2639 void tst_qquicktextedit::getFormattedText_data()
2641 QTest::addColumn<QString>("text");
2642 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
2643 QTest::addColumn<int>("start");
2644 QTest::addColumn<int>("end");
2645 QTest::addColumn<QString>("expectedText");
2647 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
2648 const QString plainBoldText = QStringLiteral("This is some bold text");
2650 QTest::newRow("all plain text")
2652 << QQuickTextEdit::PlainText
2653 << 0 << standard.at(0).length()
2656 QTest::newRow("plain text sub string")
2658 << QQuickTextEdit::PlainText
2660 << standard.at(0).mid(0, 12);
2662 QTest::newRow("plain text sub string reversed")
2664 << QQuickTextEdit::PlainText
2666 << standard.at(0).mid(0, 12);
2668 QTest::newRow("plain text cropped beginning")
2670 << QQuickTextEdit::PlainText
2672 << standard.at(0).mid(0, 4);
2674 QTest::newRow("plain text cropped end")
2676 << QQuickTextEdit::PlainText
2677 << 23 << standard.at(0).length() + 8
2678 << standard.at(0).mid(23);
2680 QTest::newRow("plain text cropped beginning and end")
2682 << QQuickTextEdit::PlainText
2683 << -9 << standard.at(0).length() + 4
2686 QTest::newRow("all rich (Auto) text")
2688 << QQuickTextEdit::AutoText
2689 << 0 << plainBoldText.length()
2690 << QString("This is some \\<.*\\>bold\\</.*\\> text");
2692 QTest::newRow("all rich (Rich) text")
2694 << QQuickTextEdit::RichText
2695 << 0 << plainBoldText.length()
2696 << QString("This is some \\<.*\\>bold\\</.*\\> text");
2698 QTest::newRow("all rich (Plain) text")
2700 << QQuickTextEdit::PlainText
2701 << 0 << richBoldText.length()
2704 QTest::newRow("rich (Auto) text sub string")
2706 << QQuickTextEdit::AutoText
2708 << QString("\\<.*\\>old\\</.*\\> tex");
2710 QTest::newRow("rich (Rich) text sub string")
2712 << QQuickTextEdit::RichText
2714 << QString("\\<.*\\>old\\</.*\\> tex");
2716 QTest::newRow("rich (Plain) text sub string")
2718 << QQuickTextEdit::PlainText
2720 << richBoldText.mid(17, 10);
2723 void tst_qquicktextedit::getFormattedText()
2725 QFETCH(QString, text);
2726 QFETCH(QQuickTextEdit::TextFormat, textFormat);
2729 QFETCH(QString, expectedText);
2731 QString componentStr = "import QtQuick 2.0\nTextEdit {}";
2732 QDeclarativeComponent textEditComponent(&engine);
2733 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2734 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2735 QVERIFY(textEdit != 0);
2737 textEdit->setTextFormat(textFormat);
2738 textEdit->setText(text);
2740 if (textFormat == QQuickTextEdit::RichText
2741 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
2742 QVERIFY(textEdit->getFormattedText(start, end).contains(QRegExp(expectedText)));
2744 QCOMPARE(textEdit->getFormattedText(start, end), expectedText);
2748 void tst_qquicktextedit::insert_data()
2750 QTest::addColumn<QString>("text");
2751 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
2752 QTest::addColumn<int>("selectionStart");
2753 QTest::addColumn<int>("selectionEnd");
2754 QTest::addColumn<int>("insertPosition");
2755 QTest::addColumn<QString>("insertText");
2756 QTest::addColumn<QString>("expectedText");
2757 QTest::addColumn<int>("expectedSelectionStart");
2758 QTest::addColumn<int>("expectedSelectionEnd");
2759 QTest::addColumn<int>("expectedCursorPosition");
2760 QTest::addColumn<bool>("selectionChanged");
2761 QTest::addColumn<bool>("cursorPositionChanged");
2763 QTest::newRow("at cursor position (beginning)")
2764 << standard.at(0) << QQuickTextEdit::PlainText
2767 << QString("Hello") + standard.at(0)
2771 QTest::newRow("at cursor position (end)")
2772 << standard.at(0) << QQuickTextEdit::PlainText
2773 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
2775 << standard.at(0) + QString("Hello")
2776 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
2779 QTest::newRow("at cursor position (middle)")
2780 << standard.at(0) << QQuickTextEdit::PlainText
2783 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2787 QTest::newRow("after cursor position (beginning)")
2788 << standard.at(0) << QQuickTextEdit::PlainText
2791 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2795 QTest::newRow("before cursor position (end)")
2796 << standard.at(0) << QQuickTextEdit::PlainText
2797 << standard.at(0).length() << standard.at(0).length() << 18
2799 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2800 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
2803 QTest::newRow("before cursor position (middle)")
2804 << standard.at(0) << QQuickTextEdit::PlainText
2807 << QString("Hello") + standard.at(0)
2811 QTest::newRow("after cursor position (middle)")
2812 << standard.at(0) << QQuickTextEdit::PlainText
2813 << 18 << 18 << standard.at(0).length()
2815 << standard.at(0) + QString("Hello")
2819 QTest::newRow("before selection")
2820 << standard.at(0) << QQuickTextEdit::PlainText
2823 << QString("Hello") + standard.at(0)
2827 QTest::newRow("before reversed selection")
2828 << standard.at(0) << QQuickTextEdit::PlainText
2831 << QString("Hello") + standard.at(0)
2835 QTest::newRow("after selection")
2836 << standard.at(0) << QQuickTextEdit::PlainText
2837 << 14 << 19 << standard.at(0).length()
2839 << standard.at(0) + QString("Hello")
2843 QTest::newRow("after reversed selection")
2844 << standard.at(0) << QQuickTextEdit::PlainText
2845 << 19 << 14 << standard.at(0).length()
2847 << standard.at(0) + QString("Hello")
2851 QTest::newRow("into selection")
2852 << standard.at(0) << QQuickTextEdit::PlainText
2855 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2859 QTest::newRow("into reversed selection")
2860 << standard.at(0) << QQuickTextEdit::PlainText
2863 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
2867 QTest::newRow("rich text into plain text")
2868 << standard.at(0) << QQuickTextEdit::PlainText
2870 << QString("<b>Hello</b>")
2871 << QString("<b>Hello</b>") + standard.at(0)
2875 QTest::newRow("rich text into rich text")
2876 << standard.at(0) << QQuickTextEdit::RichText
2878 << QString("<b>Hello</b>")
2879 << QString("Hello") + standard.at(0)
2883 QTest::newRow("rich text into auto text")
2884 << standard.at(0) << QQuickTextEdit::AutoText
2886 << QString("<b>Hello</b>")
2887 << QString("Hello") + standard.at(0)
2891 QTest::newRow("before start")
2892 << standard.at(0) << QQuickTextEdit::PlainText
2899 QTest::newRow("past end")
2900 << standard.at(0) << QQuickTextEdit::PlainText
2901 << 0 << 0 << standard.at(0).length() + 3
2908 void tst_qquicktextedit::insert()
2910 QFETCH(QString, text);
2911 QFETCH(QQuickTextEdit::TextFormat, textFormat);
2912 QFETCH(int, selectionStart);
2913 QFETCH(int, selectionEnd);
2914 QFETCH(int, insertPosition);
2915 QFETCH(QString, insertText);
2916 QFETCH(QString, expectedText);
2917 QFETCH(int, expectedSelectionStart);
2918 QFETCH(int, expectedSelectionEnd);
2919 QFETCH(int, expectedCursorPosition);
2920 QFETCH(bool, selectionChanged);
2921 QFETCH(bool, cursorPositionChanged);
2923 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
2924 QDeclarativeComponent textEditComponent(&engine);
2925 textEditComponent.setData(componentStr.toLatin1(), QUrl());
2926 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2927 QVERIFY(textEdit != 0);
2929 textEdit->setTextFormat(textFormat);
2930 textEdit->select(selectionStart, selectionEnd);
2932 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
2933 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
2934 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
2935 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
2936 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
2938 textEdit->insert(insertPosition, insertText);
2940 if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
2941 Qt::mightBeRichText(text) || Qt::mightBeRichText(insertText)))) {
2942 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
2944 QCOMPARE(textEdit->text(), expectedText);
2947 QCOMPARE(textEdit->length(), expectedText.length());
2949 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
2950 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
2951 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
2953 if (selectionStart > selectionEnd)
2954 qSwap(selectionStart, selectionEnd);
2956 QEXPECT_FAIL("into selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
2957 QEXPECT_FAIL("into reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
2958 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
2959 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
2960 QEXPECT_FAIL("into reversed selection", "selectionEndChanged signal not emitted", Continue);
2961 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
2962 QCOMPARE(textSpy.count() > 0, text != expectedText);
2963 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
2966 void tst_qquicktextedit::remove_data()
2968 QTest::addColumn<QString>("text");
2969 QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
2970 QTest::addColumn<int>("selectionStart");
2971 QTest::addColumn<int>("selectionEnd");
2972 QTest::addColumn<int>("removeStart");
2973 QTest::addColumn<int>("removeEnd");
2974 QTest::addColumn<QString>("expectedText");
2975 QTest::addColumn<int>("expectedSelectionStart");
2976 QTest::addColumn<int>("expectedSelectionEnd");
2977 QTest::addColumn<int>("expectedCursorPosition");
2978 QTest::addColumn<bool>("selectionChanged");
2979 QTest::addColumn<bool>("cursorPositionChanged");
2981 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
2982 const QString plainBoldText = QStringLiteral("This is some bold text");
2984 QTest::newRow("from cursor position (beginning)")
2985 << standard.at(0) << QQuickTextEdit::PlainText
2988 << standard.at(0).mid(5)
2992 QTest::newRow("to cursor position (beginning)")
2993 << standard.at(0) << QQuickTextEdit::PlainText
2996 << standard.at(0).mid(5)
3000 QTest::newRow("to cursor position (end)")
3001 << standard.at(0) << QQuickTextEdit::PlainText
3002 << standard.at(0).length() << standard.at(0).length()
3003 << standard.at(0).length() << standard.at(0).length() - 5
3004 << standard.at(0).mid(0, standard.at(0).length() - 5)
3005 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3008 QTest::newRow("to cursor position (end)")
3009 << standard.at(0) << QQuickTextEdit::PlainText
3010 << standard.at(0).length() << standard.at(0).length()
3011 << standard.at(0).length() - 5 << standard.at(0).length()
3012 << standard.at(0).mid(0, standard.at(0).length() - 5)
3013 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3016 QTest::newRow("from cursor position (middle)")
3017 << standard.at(0) << QQuickTextEdit::PlainText
3020 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3024 QTest::newRow("to cursor position (middle)")
3025 << standard.at(0) << QQuickTextEdit::PlainText
3028 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3032 QTest::newRow("after cursor position (beginning)")
3033 << standard.at(0) << QQuickTextEdit::PlainText
3036 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3040 QTest::newRow("before cursor position (end)")
3041 << standard.at(0) << QQuickTextEdit::PlainText
3042 << standard.at(0).length() << standard.at(0).length()
3044 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3045 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3048 QTest::newRow("before cursor position (middle)")
3049 << standard.at(0) << QQuickTextEdit::PlainText
3052 << standard.at(0).mid(5)
3056 QTest::newRow("after cursor position (middle)")
3057 << standard.at(0) << QQuickTextEdit::PlainText
3060 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3064 QTest::newRow("before selection")
3065 << standard.at(0) << QQuickTextEdit::PlainText
3068 << standard.at(0).mid(5)
3072 QTest::newRow("before reversed selection")
3073 << standard.at(0) << QQuickTextEdit::PlainText
3076 << standard.at(0).mid(5)
3080 QTest::newRow("after selection")
3081 << standard.at(0) << QQuickTextEdit::PlainText
3083 << standard.at(0).length() - 5 << standard.at(0).length()
3084 << standard.at(0).mid(0, standard.at(0).length() - 5)
3088 QTest::newRow("after reversed selection")
3089 << standard.at(0) << QQuickTextEdit::PlainText
3091 << standard.at(0).length() - 5 << standard.at(0).length()
3092 << standard.at(0).mid(0, standard.at(0).length() - 5)
3096 QTest::newRow("from selection")
3097 << standard.at(0) << QQuickTextEdit::PlainText
3100 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3104 QTest::newRow("from reversed selection")
3105 << standard.at(0) << QQuickTextEdit::PlainText
3108 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3112 QTest::newRow("plain text cropped beginning")
3113 << standard.at(0) << QQuickTextEdit::PlainText
3116 << standard.at(0).mid(4)
3120 QTest::newRow("plain text cropped end")
3121 << standard.at(0) << QQuickTextEdit::PlainText
3123 << 23 << standard.at(0).length() + 8
3124 << standard.at(0).mid(0, 23)
3128 QTest::newRow("plain text cropped beginning and end")
3129 << standard.at(0) << QQuickTextEdit::PlainText
3131 << -9 << standard.at(0).length() + 4
3136 QTest::newRow("all rich text")
3137 << richBoldText << QQuickTextEdit::RichText
3139 << 0 << plainBoldText.length()
3144 QTest::newRow("rick text sub string")
3145 << richBoldText << QQuickTextEdit::RichText
3148 << plainBoldText.mid(0, 14) + plainBoldText.mid(21)
3153 void tst_qquicktextedit::remove()
3155 QFETCH(QString, text);
3156 QFETCH(QQuickTextEdit::TextFormat, textFormat);
3157 QFETCH(int, selectionStart);
3158 QFETCH(int, selectionEnd);
3159 QFETCH(int, removeStart);
3160 QFETCH(int, removeEnd);
3161 QFETCH(QString, expectedText);
3162 QFETCH(int, expectedSelectionStart);
3163 QFETCH(int, expectedSelectionEnd);
3164 QFETCH(int, expectedCursorPosition);
3165 QFETCH(bool, selectionChanged);
3166 QFETCH(bool, cursorPositionChanged);
3168 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3169 QDeclarativeComponent textEditComponent(&engine);
3170 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3171 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3172 QVERIFY(textEdit != 0);
3174 textEdit->setTextFormat(textFormat);
3175 textEdit->select(selectionStart, selectionEnd);
3177 QSignalSpy selectionSpy(textEdit, SIGNAL(selectionChanged()));
3178 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3179 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3180 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3181 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3183 textEdit->remove(removeStart, removeEnd);
3185 if (textFormat == QQuickTextEdit::RichText
3186 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3187 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3189 QCOMPARE(textEdit->text(), expectedText);
3191 QCOMPARE(textEdit->length(), expectedText.length());
3193 if (selectionStart > selectionEnd) //
3194 qSwap(selectionStart, selectionEnd);
3196 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3197 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3198 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3200 QEXPECT_FAIL("from selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3201 QEXPECT_FAIL("from reversed selection", "selectionChanged signal isn't emitted on edits within selection", Continue);
3202 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3203 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3204 QEXPECT_FAIL("from reversed selection", "selectionEndChanged signal not emitted", Continue);
3205 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3206 QCOMPARE(textSpy.count() > 0, text != expectedText);
3209 if (cursorPositionChanged) //
3210 QVERIFY(cursorPositionSpy.count() > 0);
3214 void tst_qquicktextedit::keySequence_data()
3216 QTest::addColumn<QString>("text");
3217 QTest::addColumn<QKeySequence>("sequence");
3218 QTest::addColumn<int>("selectionStart");
3219 QTest::addColumn<int>("selectionEnd");
3220 QTest::addColumn<int>("cursorPosition");
3221 QTest::addColumn<QString>("expectedText");
3222 QTest::addColumn<QString>("selectedText");
3224 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3226 QTest::newRow("select all")
3227 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3228 << 44 << standard.at(0) << standard.at(0);
3229 QTest::newRow("select end of line")
3230 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3231 << 44 << standard.at(0) << standard.at(0).mid(5);
3232 QTest::newRow("select end of document")
3233 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3234 << 44 << standard.at(0) << standard.at(0).mid(3);
3235 QTest::newRow("select end of block")
3236 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3237 << 44 << standard.at(0) << standard.at(0).mid(18);
3238 QTest::newRow("delete end of line")
3239 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3240 << 24 << standard.at(0).mid(0, 24) << QString();
3241 QTest::newRow("move to start of line")
3242 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3243 << 0 << standard.at(0) << QString();
3244 QTest::newRow("move to start of block")
3245 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3246 << 0 << standard.at(0) << QString();
3247 QTest::newRow("move to next char")
3248 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3249 << 13 << standard.at(0) << QString();
3250 QTest::newRow("move to previous char")
3251 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3252 << 2 << standard.at(0) << QString();
3253 QTest::newRow("select next char")
3254 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3255 << 24 << standard.at(0) << standard.at(0).mid(23, 1);
3256 QTest::newRow("select previous char")
3257 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
3258 << 18 << standard.at(0) << standard.at(0).mid(18, 1);
3259 QTest::newRow("move to next word")
3260 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
3261 << 10 << standard.at(0) << QString();
3262 QTest::newRow("move to previous word")
3263 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
3264 << 4 << standard.at(0) << QString();
3265 QTest::newRow("select previous word")
3266 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
3267 << 10 << standard.at(0) << standard.at(0).mid(10, 1);
3268 QTest::newRow("delete (selection)")
3269 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
3270 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
3271 QTest::newRow("delete (no selection)")
3272 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
3273 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
3274 QTest::newRow("delete end of word")
3275 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
3276 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
3277 QTest::newRow("delete start of word")
3278 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
3279 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
3282 void tst_qquicktextedit::keySequence()
3284 QFETCH(QString, text);
3285 QFETCH(QKeySequence, sequence);
3286 QFETCH(int, selectionStart);
3287 QFETCH(int, selectionEnd);
3288 QFETCH(int, cursorPosition);
3289 QFETCH(QString, expectedText);
3290 QFETCH(QString, selectedText);
3292 if (sequence.isEmpty()) {
3293 QSKIP("Key sequence is undefined");
3296 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; text: \"" + text + "\" }";
3297 QDeclarativeComponent textEditComponent(&engine);
3298 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3299 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3300 QVERIFY(textEdit != 0);
3302 QQuickCanvas canvas;
3303 textEdit->setParentItem(canvas.rootItem());
3305 canvas.requestActivateWindow();
3306 QTest::qWaitForWindowShown(&canvas);
3307 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3309 textEdit->select(selectionStart, selectionEnd);
3311 simulateKeys(&canvas, sequence);
3313 QCOMPARE(textEdit->cursorPosition(), cursorPosition);
3314 QCOMPARE(textEdit->text(), expectedText);
3315 QCOMPARE(textEdit->selectedText(), selectedText);
3319 #define REPLACE_UNTIL_END 1
3321 void tst_qquicktextedit::undo_data()
3323 QTest::addColumn<QStringList>("insertString");
3324 QTest::addColumn<IntList>("insertIndex");
3325 QTest::addColumn<IntList>("insertMode");
3326 QTest::addColumn<QStringList>("expectedString");
3327 QTest::addColumn<bool>("use_keys");
3329 for (int i=0; i<2; i++) {
3330 QString keys_str = "keyboard";
3331 bool use_keys = true;
3333 keys_str = "insert";
3338 IntList insertIndex;
3340 QStringList insertString;
3341 QStringList expectedString;
3344 insertMode << NORMAL;
3345 insertString << "1";
3348 insertMode << NORMAL;
3349 insertString << "5";
3352 insertMode << NORMAL;
3353 insertString << "3";
3356 insertMode << NORMAL;
3357 insertString << "2";
3360 insertMode << NORMAL;
3361 insertString << "4";
3363 expectedString << "12345";
3364 expectedString << "1235";
3365 expectedString << "135";
3366 expectedString << "15";
3367 expectedString << "";
3369 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
3377 IntList insertIndex;
3379 QStringList insertString;
3380 QStringList expectedString;
3383 insertMode << NORMAL;
3384 insertString << "World"; // World
3387 insertMode << NORMAL;
3388 insertString << "Hello"; // HelloWorld
3391 insertMode << NORMAL;
3392 insertString << "Well"; // WellHelloWorld
3395 insertMode << NORMAL;
3396 insertString << "There"; // WellHelloThereWorld;
3398 expectedString << "WellHelloThereWorld";
3399 expectedString << "WellHelloWorld";
3400 expectedString << "HelloWorld";
3401 expectedString << "World";
3402 expectedString << "";
3404 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
3412 IntList insertIndex;
3414 QStringList insertString;
3415 QStringList expectedString;
3418 insertMode << NORMAL;
3419 insertString << "Ensuring";
3422 insertMode << NORMAL;
3423 insertString << " instan";
3426 insertMode << NORMAL;
3427 insertString << "an ";
3430 insertMode << REPLACE_UNTIL_END;
3431 insertString << " unique instance.";
3433 expectedString << "Ensuring a unique instance.";
3434 expectedString << "Ensuring a "; // ### Not present in TextInput.
3435 expectedString << "Ensuring an instan";
3436 expectedString << "Ensuring instan";
3437 expectedString << "";
3439 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
3449 void tst_qquicktextedit::undo()
3451 QFETCH(QStringList, insertString);
3452 QFETCH(IntList, insertIndex);
3453 QFETCH(IntList, insertMode);
3454 QFETCH(QStringList, expectedString);
3456 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3457 QDeclarativeComponent textEditComponent(&engine);
3458 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3459 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3460 QVERIFY(textEdit != 0);
3462 QQuickCanvas canvas;
3463 textEdit->setParentItem(canvas.rootItem());
3465 canvas.requestActivateWindow();
3466 QTest::qWaitForWindowShown(&canvas);
3467 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3469 QVERIFY(!textEdit->canUndo());
3471 QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
3475 // STEP 1: First build up an undo history by inserting or typing some strings...
3476 for (i = 0; i < insertString.size(); ++i) {
3477 if (insertIndex[i] > -1)
3478 textEdit->setCursorPosition(insertIndex[i]);
3480 // experimental stuff
3481 if (insertMode[i] == REPLACE_UNTIL_END) {
3482 textEdit->select(insertIndex[i], insertIndex[i] + 8);
3484 // This is what I actually want...
3485 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
3488 for (int j = 0; j < insertString.at(i).length(); j++)
3489 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3492 QCOMPARE(spy.count(), 1);
3494 // STEP 2: Next call undo several times and see if we can restore to the previous state
3495 for (i = 0; i < expectedString.size() - 1; ++i) {
3496 QCOMPARE(textEdit->text(), expectedString[i]);
3497 QVERIFY(textEdit->canUndo());
3501 // STEP 3: Verify that we have undone everything
3502 QVERIFY(textEdit->text().isEmpty());
3503 QVERIFY(!textEdit->canUndo());
3504 QCOMPARE(spy.count(), 2);
3507 void tst_qquicktextedit::redo_data()
3509 QTest::addColumn<QStringList>("insertString");
3510 QTest::addColumn<IntList>("insertIndex");
3511 QTest::addColumn<QStringList>("expectedString");
3514 IntList insertIndex;
3515 QStringList insertString;
3516 QStringList expectedString;
3519 insertString << "World"; // World
3521 insertString << "Hello"; // HelloWorld
3523 insertString << "Well"; // WellHelloWorld
3525 insertString << "There"; // WellHelloThereWorld;
3527 expectedString << "World";
3528 expectedString << "HelloWorld";
3529 expectedString << "WellHelloWorld";
3530 expectedString << "WellHelloThereWorld";
3532 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
3536 void tst_qquicktextedit::redo()
3538 QFETCH(QStringList, insertString);
3539 QFETCH(IntList, insertIndex);
3540 QFETCH(QStringList, expectedString);
3542 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3543 QDeclarativeComponent textEditComponent(&engine);
3544 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3545 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3546 QVERIFY(textEdit != 0);
3548 QQuickCanvas canvas;
3549 textEdit->setParentItem(canvas.rootItem());
3551 canvas.requestActivateWindow();
3552 QTest::qWaitForWindowShown(&canvas);
3553 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3555 QVERIFY(!textEdit->canUndo());
3556 QVERIFY(!textEdit->canRedo());
3558 QSignalSpy spy(textEdit, SIGNAL(canRedoChanged()));
3561 // inserts the diff strings at diff positions
3562 for (i = 0; i < insertString.size(); ++i) {
3563 if (insertIndex[i] > -1)
3564 textEdit->setCursorPosition(insertIndex[i]);
3565 for (int j = 0; j < insertString.at(i).length(); j++)
3566 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3567 QVERIFY(textEdit->canUndo());
3568 QVERIFY(!textEdit->canRedo());
3571 QCOMPARE(spy.count(), 0);
3574 while (!textEdit->text().isEmpty()) {
3575 QVERIFY(textEdit->canUndo());
3577 QVERIFY(textEdit->canRedo());
3580 QCOMPARE(spy.count(), 1);
3582 for (i = 0; i < expectedString.size(); ++i) {
3583 QVERIFY(textEdit->canRedo());
3585 QCOMPARE(textEdit->text() , expectedString[i]);
3586 QVERIFY(textEdit->canUndo());
3588 QVERIFY(!textEdit->canRedo());
3589 QCOMPARE(spy.count(), 2);
3592 void tst_qquicktextedit::undo_keypressevents_data()
3594 QTest::addColumn<KeyList>("keys");
3595 QTest::addColumn<QStringList>("expectedString");
3599 QStringList expectedString;
3612 expectedString << "BEVERYAFRAID!";
3613 expectedString << "BEVERYAFRAID";
3614 expectedString << "VERYAFRAID";
3615 expectedString << "AFRAID";
3617 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
3620 QStringList expectedString;
3623 keys << "1234" << Qt::Key_Home
3625 << Qt::Key_Right << Qt::Key_Right
3627 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3631 expectedString << "12";
3632 expectedString << "1234";
3634 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
3637 QStringList expectedString;
3643 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3645 << QKeySequence::Undo
3646 // ### Text is selected in text input
3648 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3651 expectedString << "AB";
3652 expectedString << "AB12";
3654 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
3657 QStringList expectedString;
3662 << Qt::Key_Left << Qt::Key_Left
3666 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3667 // overwriting '1234' with '5'
3669 // undoing deletion of 'AB'
3670 << QKeySequence::Undo
3671 // ### Text is selected in text input
3672 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3673 // overwriting '1234' with '6'
3676 expectedString << "ab6cd";
3677 // for versions previous to 3.2 we overwrite needed two undo operations
3678 expectedString << "ab1234cd";
3679 expectedString << "abcd";
3681 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
3684 QStringList expectedString;
3689 << Qt::Key_Backspace;
3691 expectedString << "AB";
3692 expectedString << "ABC";
3694 QTest::newRow("Inserts,backspace") << keys << expectedString;
3697 QStringList expectedString;
3701 << Qt::Key_Backspace
3705 expectedString << "ABZ";
3706 expectedString << "AB";
3707 expectedString << "ABC";
3709 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
3712 QStringList expectedString;
3715 keys << "123" << Qt::Key_Home
3717 << (Qt::Key_End | Qt::ShiftModifier)
3718 // overwriting '123' with 'ABC'
3721 expectedString << "ABC";
3722 // ### One operation in TextInput.
3723 expectedString << "A";
3724 expectedString << "123";
3726 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
3730 void tst_qquicktextedit::undo_keypressevents()
3732 QFETCH(KeyList, keys);
3733 QFETCH(QStringList, expectedString);
3735 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
3736 QDeclarativeComponent textEditComponent(&engine);
3737 textEditComponent.setData(componentStr.toLatin1(), QUrl());
3738 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3739 QVERIFY(textEdit != 0);
3741 QQuickCanvas canvas;
3742 textEdit->setParentItem(canvas.rootItem());
3744 canvas.requestActivateWindow();
3745 QTest::qWaitForWindowShown(&canvas);
3746 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3748 simulateKeys(&canvas, keys);
3750 for (int i = 0; i < expectedString.size(); ++i) {
3751 QCOMPARE(textEdit->text() , expectedString[i]);
3754 QVERIFY(textEdit->text().isEmpty());
3757 void tst_qquicktextedit::baseUrl()
3759 QUrl localUrl("file:///tests/text.qml");
3760 QUrl remoteUrl("http://qt.nokia.com/test.qml");
3762 QDeclarativeComponent textComponent(&engine);
3763 textComponent.setData("import QtQuick 2.0\n TextEdit {}", localUrl);
3764 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(textComponent.create());
3766 QCOMPARE(textObject->baseUrl(), localUrl);
3768 QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
3770 textObject->setBaseUrl(localUrl);
3771 QCOMPARE(textObject->baseUrl(), localUrl);
3772 QCOMPARE(spy.count(), 0);
3774 textObject->setBaseUrl(remoteUrl);
3775 QCOMPARE(textObject->baseUrl(), remoteUrl);
3776 QCOMPARE(spy.count(), 1);
3778 textObject->resetBaseUrl();
3779 QCOMPARE(textObject->baseUrl(), localUrl);
3780 QCOMPARE(spy.count(), 2);
3783 void tst_qquicktextedit::embeddedImages_data()
3785 QTest::addColumn<QUrl>("qmlfile");
3786 QTest::addColumn<QString>("error");
3787 QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
3788 QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
3789 << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
3790 QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
3791 QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
3792 QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
3793 << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
3794 QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
3797 void tst_qquicktextedit::embeddedImages()
3799 QFETCH(QUrl, qmlfile);
3800 QFETCH(QString, error);
3802 TestHTTPServer server(42332);
3803 server.serveDirectory(testFile("http"));
3805 if (!error.isEmpty())
3806 QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
3808 QDeclarativeComponent textComponent(&engine, qmlfile);
3809 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
3811 QVERIFY(textObject != 0);
3812 QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
3814 QPixmap pm(testFile("http/exists.png"));
3815 if (error.isEmpty()) {
3816 QCOMPARE(textObject->width(), double(pm.width()));
3817 QCOMPARE(textObject->height(), double(pm.height()));
3819 QVERIFY(16 != pm.width()); // check test is effective
3820 QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
3821 QCOMPARE(textObject->height(), 16.0);
3827 void tst_qquicktextedit::emptytags_QTBUG_22058()
3829 QQuickView canvas(testFileUrl("qtbug-22058.qml"));
3830 QVERIFY(canvas.rootObject() != 0);
3833 canvas.requestActivateWindow();
3834 QTest::qWaitForWindowShown(&canvas);
3835 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("inputField")));
3836 QVERIFY(input->hasActiveFocus());
3838 QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
3839 event.setCommitString("<b>Bold<");
3840 QGuiApplication::sendEvent(input, &event);
3841 QCOMPARE(input->text(), QString("<b>Bold<"));
3842 event.setCommitString(">");
3843 QGuiApplication::sendEvent(input, &event);
3844 QCOMPARE(input->text(), QString("<b>Bold<>"));
3847 QTEST_MAIN(tst_qquicktextedit)
3849 #include "tst_qquicktextedit.moc"