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/util.h"
44 #include <private/qinputmethod_p.h>
45 #include <QtQml/qqmlengine.h>
46 #include <QtQml/qqmlexpression.h>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputMethod>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
59 #include <Carbon/Carbon.h>
62 #include "qplatformdefs.h"
63 #include "../../shared/platforminputcontext.h"
65 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
66 Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
67 Q_DECLARE_METATYPE(Qt::Key)
69 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
71 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
73 // XXX This will be replaced by some clever persistent platform image store.
74 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
75 QString arch = "unknown-architecture"; // QTest needs to help with this.
77 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
79 if (!QFile::exists(expectfile)) {
80 actual.save(expectfile);
81 qWarning() << "created" << expectfile;
87 template <typename T> static T evaluate(QObject *scope, const QString &expression)
89 QQmlExpression expr(qmlContext(scope), scope, expression);
90 T result = expr.evaluate().value<T>();
92 qWarning() << expr.error().toString();
96 template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
98 typedef QPair<int, QChar> Key;
100 class tst_qquicktextinput : public QQmlDataTest
105 tst_qquicktextinput();
115 void persistentSelection();
116 void isRightToLeft_data();
117 void isRightToLeft();
118 void moveCursorSelection_data();
119 void moveCursorSelection();
120 void moveCursorSelectionSequence_data();
121 void moveCursorSelectionSequence();
122 void dragMouseSelection();
123 void mouseSelectionMode_data();
124 void mouseSelectionMode();
125 void tripleClickSelectsAll();
127 void horizontalAlignment_data();
128 void horizontalAlignment();
129 void horizontalAlignment_RightToLeft();
130 void verticalAlignment();
142 void passwordCharacter();
143 void cursorDelegate_data();
144 void cursorDelegate();
145 void cursorVisible();
146 void cursorRectangle_data();
147 void cursorRectangle();
149 void navigation_RTL();
151 void copyAndPasteKeySequence();
152 void canPasteEmpty();
157 void openInputPanel();
158 void setHAlignClearCache();
159 void focusOutClearSelection();
162 void passwordEchoDelay();
163 void geometrySignals();
166 void preeditAutoScroll();
167 void preeditCursorRectangle();
168 void inputContextMouseHandler();
169 void inputMethodComposing();
170 void inputMethodUpdate();
171 void cursorRectangleSize();
180 void keySequence_data();
187 void undo_keypressevents_data();
188 void undo_keypressevents();
191 void QTBUG_19956_data();
192 void QTBUG_19956_regexp();
194 void implicitSize_data();
196 void implicitSizeBinding_data();
197 void implicitSizeBinding();
199 void negativeDimensions();
202 void setInputMask_data();
204 void inputMask_data();
206 void clearInputMask();
207 void keypress_inputMask_data();
208 void keypress_inputMask();
209 void hasAcceptableInputMask_data();
210 void hasAcceptableInputMask();
211 void maskCharacter_data();
212 void maskCharacter();
215 void simulateKey(QWindow *, int key);
217 void simulateKeys(QWindow *window, const QList<Key> &keys);
218 void simulateKeys(QWindow *window, const QKeySequence &sequence);
221 QStringList standard;
222 QStringList colorStrings;
225 typedef QList<int> IntList;
226 Q_DECLARE_METATYPE(IntList)
228 typedef QList<Key> KeyList;
229 Q_DECLARE_METATYPE(KeyList)
231 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
233 for (int i = 0; i < keys.count(); ++i) {
234 const int key = keys.at(i).first;
235 const int modifiers = key & Qt::KeyboardModifierMask;
236 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
238 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
239 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
241 QGuiApplication::sendEvent(window, &press);
242 QGuiApplication::sendEvent(window, &release);
246 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
248 for (int i = 0; i < sequence.count(); ++i) {
249 const int key = sequence[i];
250 const int modifiers = key & Qt::KeyboardModifierMask;
252 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
256 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
258 for (int i = 0; i < sequence.count(); ++i)
259 keys << Key(sequence[i], QChar());
263 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
265 for (int i = 0; i < N - 1; ++i) {
266 int key = QTest::asciiToKey(characters[i]);
267 QChar character = QLatin1Char(characters[i]);
268 keys << Key(key, character);
273 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
275 keys << Key(key, QChar());
279 void tst_qquicktextinput::cleanup()
281 // ensure not even skipped tests with custom input context leave it dangling
282 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
283 inputMethodPrivate->testContext = 0;
286 tst_qquicktextinput::tst_qquicktextinput()
288 standard << "the quick brown fox jumped over the lazy dog"
289 << "It's supercalifragisiticexpialidocious!"
294 colorStrings << "aliceblue"
308 void tst_qquicktextinput::text()
311 QQmlComponent textinputComponent(&engine);
312 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
313 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
315 QVERIFY(textinputObject != 0);
316 QCOMPARE(textinputObject->text(), QString(""));
317 QCOMPARE(textinputObject->length(), 0);
319 delete textinputObject;
322 for (int i = 0; i < standard.size(); i++)
324 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
325 QQmlComponent textinputComponent(&engine);
326 textinputComponent.setData(componentStr.toLatin1(), QUrl());
327 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
329 QVERIFY(textinputObject != 0);
330 QCOMPARE(textinputObject->text(), standard.at(i));
331 QCOMPARE(textinputObject->length(), standard.at(i).length());
333 delete textinputObject;
338 void tst_qquicktextinput::width()
340 // uses Font metrics to find the width for standard
342 QQmlComponent textinputComponent(&engine);
343 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
344 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
346 QVERIFY(textinputObject != 0);
347 QCOMPARE(textinputObject->width(), 0.0);
349 delete textinputObject;
352 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
354 for (int i = 0; i < standard.size(); i++)
356 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
357 QQmlComponent textinputComponent(&engine);
358 textinputComponent.setData(componentStr.toLatin1(), QUrl());
359 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
361 QString s = standard.at(i);
362 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
364 QTextLayout layout(s);
365 layout.setFont(textinputObject->font());
366 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
367 if (requiresUnhintedMetrics) {
369 option.setUseDesignMetrics(true);
370 layout.setTextOption(option);
373 layout.beginLayout();
375 QTextLine line = layout.createLine();
382 qreal metricWidth = ceil(layout.boundingRect().width());
384 QVERIFY(textinputObject != 0);
385 int delta = abs(int(int(textinputObject->width()) - metricWidth));
386 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
388 delete textinputObject;
392 void tst_qquicktextinput::font()
394 //test size, then bold, then italic, then family
396 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
397 QQmlComponent textinputComponent(&engine);
398 textinputComponent.setData(componentStr.toLatin1(), QUrl());
399 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
401 QVERIFY(textinputObject != 0);
402 QCOMPARE(textinputObject->font().pointSize(), 40);
403 QCOMPARE(textinputObject->font().bold(), false);
404 QCOMPARE(textinputObject->font().italic(), false);
406 delete textinputObject;
410 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
411 QQmlComponent textinputComponent(&engine);
412 textinputComponent.setData(componentStr.toLatin1(), QUrl());
413 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
415 QVERIFY(textinputObject != 0);
416 QCOMPARE(textinputObject->font().bold(), true);
417 QCOMPARE(textinputObject->font().italic(), false);
419 delete textinputObject;
423 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
424 QQmlComponent textinputComponent(&engine);
425 textinputComponent.setData(componentStr.toLatin1(), QUrl());
426 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
428 QVERIFY(textinputObject != 0);
429 QCOMPARE(textinputObject->font().italic(), true);
430 QCOMPARE(textinputObject->font().bold(), false);
432 delete textinputObject;
436 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
437 QQmlComponent textinputComponent(&engine);
438 textinputComponent.setData(componentStr.toLatin1(), QUrl());
439 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
441 QVERIFY(textinputObject != 0);
442 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
443 QCOMPARE(textinputObject->font().bold(), false);
444 QCOMPARE(textinputObject->font().italic(), false);
446 delete textinputObject;
450 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
451 QQmlComponent textinputComponent(&engine);
452 textinputComponent.setData(componentStr.toLatin1(), QUrl());
453 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
455 QVERIFY(textinputObject != 0);
456 QCOMPARE(textinputObject->font().family(), QString(""));
458 delete textinputObject;
462 void tst_qquicktextinput::color()
465 for (int i = 0; i < colorStrings.size(); i++)
467 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
468 QQmlComponent textinputComponent(&engine);
469 textinputComponent.setData(componentStr.toLatin1(), QUrl());
470 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
471 QVERIFY(textinputObject != 0);
472 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
474 delete textinputObject;
477 //test selection color
478 for (int i = 0; i < colorStrings.size(); i++)
480 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
481 QQmlComponent textinputComponent(&engine);
482 textinputComponent.setData(componentStr.toLatin1(), QUrl());
483 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
484 QVERIFY(textinputObject != 0);
485 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
487 delete textinputObject;
490 //test selected text color
491 for (int i = 0; i < colorStrings.size(); i++)
493 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
494 QQmlComponent textinputComponent(&engine);
495 textinputComponent.setData(componentStr.toLatin1(), QUrl());
496 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
497 QVERIFY(textinputObject != 0);
498 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
500 delete textinputObject;
504 QString colorStr = "#AA001234";
505 QColor testColor("#001234");
506 testColor.setAlpha(170);
508 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
509 QQmlComponent textinputComponent(&engine);
510 textinputComponent.setData(componentStr.toLatin1(), QUrl());
511 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
513 QVERIFY(textinputObject != 0);
514 QCOMPARE(textinputObject->color(), testColor);
516 delete textinputObject;
520 void tst_qquicktextinput::wrap()
523 // for specified width and wrap set true
525 QQmlComponent textComponent(&engine);
526 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
527 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
528 textHeight = textObject->height();
530 QVERIFY(textObject != 0);
531 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
532 QCOMPARE(textObject->width(), 300.);
537 for (int i = 0; i < standard.count(); i++) {
538 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
539 QQmlComponent textComponent(&engine);
540 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
541 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
543 QVERIFY(textObject != 0);
544 QCOMPARE(textObject->width(), 30.);
545 QVERIFY(textObject->height() > textHeight);
547 int oldHeight = textObject->height();
548 textObject->setWidth(100);
549 QVERIFY(textObject->height() < oldHeight);
555 void tst_qquicktextinput::selection()
557 QString testStr = standard[0];
558 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
559 QQmlComponent textinputComponent(&engine);
560 textinputComponent.setData(componentStr.toLatin1(), QUrl());
561 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
562 QVERIFY(textinputObject != 0);
565 //Test selection follows cursor
566 for (int i=0; i<= testStr.size(); i++) {
567 textinputObject->setCursorPosition(i);
568 QCOMPARE(textinputObject->cursorPosition(), i);
569 QCOMPARE(textinputObject->selectionStart(), i);
570 QCOMPARE(textinputObject->selectionEnd(), i);
571 QVERIFY(textinputObject->selectedText().isNull());
574 textinputObject->setCursorPosition(0);
575 QVERIFY(textinputObject->cursorPosition() == 0);
576 QVERIFY(textinputObject->selectionStart() == 0);
577 QVERIFY(textinputObject->selectionEnd() == 0);
578 QVERIFY(textinputObject->selectedText().isNull());
580 // Verify invalid positions are ignored.
581 textinputObject->setCursorPosition(-1);
582 QVERIFY(textinputObject->cursorPosition() == 0);
583 QVERIFY(textinputObject->selectionStart() == 0);
584 QVERIFY(textinputObject->selectionEnd() == 0);
585 QVERIFY(textinputObject->selectedText().isNull());
587 textinputObject->setCursorPosition(textinputObject->text().count()+1);
588 QVERIFY(textinputObject->cursorPosition() == 0);
589 QVERIFY(textinputObject->selectionStart() == 0);
590 QVERIFY(textinputObject->selectionEnd() == 0);
591 QVERIFY(textinputObject->selectedText().isNull());
594 for (int i=0; i<= testStr.size(); i++) {
595 textinputObject->select(0,i);
596 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
598 for (int i=0; i<= testStr.size(); i++) {
599 textinputObject->select(i,testStr.size());
600 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
603 textinputObject->setCursorPosition(0);
604 QVERIFY(textinputObject->cursorPosition() == 0);
605 QVERIFY(textinputObject->selectionStart() == 0);
606 QVERIFY(textinputObject->selectionEnd() == 0);
607 QVERIFY(textinputObject->selectedText().isNull());
609 //Test Error Ignoring behaviour
610 textinputObject->setCursorPosition(0);
611 QVERIFY(textinputObject->selectedText().isNull());
612 textinputObject->select(-10,0);
613 QVERIFY(textinputObject->selectedText().isNull());
614 textinputObject->select(100,110);
615 QVERIFY(textinputObject->selectedText().isNull());
616 textinputObject->select(0,-10);
617 QVERIFY(textinputObject->selectedText().isNull());
618 textinputObject->select(0,100);
619 QVERIFY(textinputObject->selectedText().isNull());
620 textinputObject->select(0,10);
621 QVERIFY(textinputObject->selectedText().size() == 10);
622 textinputObject->select(-10,10);
623 QVERIFY(textinputObject->selectedText().size() == 10);
624 textinputObject->select(100,101);
625 QVERIFY(textinputObject->selectedText().size() == 10);
626 textinputObject->select(0,-10);
627 QVERIFY(textinputObject->selectedText().size() == 10);
628 textinputObject->select(0,100);
629 QVERIFY(textinputObject->selectedText().size() == 10);
631 textinputObject->deselect();
632 QVERIFY(textinputObject->selectedText().isNull());
633 textinputObject->select(0,10);
634 QVERIFY(textinputObject->selectedText().size() == 10);
635 textinputObject->deselect();
636 QVERIFY(textinputObject->selectedText().isNull());
638 // test input method selection
639 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
640 textinputObject->setFocus(true);
642 QList<QInputMethodEvent::Attribute> attributes;
643 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
644 QInputMethodEvent event("", attributes);
645 QGuiApplication::sendEvent(textinputObject, &event);
647 QCOMPARE(selectionSpy.count(), 1);
648 QCOMPARE(textinputObject->selectionStart(), 12);
649 QCOMPARE(textinputObject->selectionEnd(), 17);
651 delete textinputObject;
654 void tst_qquicktextinput::persistentSelection()
656 QQuickView canvas(testFileUrl("persistentSelection.qml"));
658 canvas.requestActivateWindow();
659 QTest::qWaitForWindowShown(&canvas);
660 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
661 canvas.requestActivateWindow();
663 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
665 QVERIFY(input->hasActiveFocus());
667 QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
669 QCOMPARE(input->persistentSelection(), false);
671 input->setPersistentSelection(false);
672 QCOMPARE(input->persistentSelection(), false);
673 QCOMPARE(spy.count(), 0);
676 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
678 input->setFocus(false);
679 QCOMPARE(input->property("selected").toString(), QString());
681 input->setFocus(true);
682 QCOMPARE(input->property("selected").toString(), QString());
684 input->setPersistentSelection(true);
685 QCOMPARE(input->persistentSelection(), true);
686 QCOMPARE(spy.count(), 1);
689 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
691 input->setFocus(false);
692 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
694 input->setFocus(true);
695 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
698 void tst_qquicktextinput::isRightToLeft_data()
700 QTest::addColumn<QString>("text");
701 QTest::addColumn<bool>("emptyString");
702 QTest::addColumn<bool>("firstCharacter");
703 QTest::addColumn<bool>("lastCharacter");
704 QTest::addColumn<bool>("middleCharacter");
705 QTest::addColumn<bool>("startString");
706 QTest::addColumn<bool>("midString");
707 QTest::addColumn<bool>("endString");
709 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
710 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
711 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
712 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
713 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
714 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;
715 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
718 void tst_qquicktextinput::isRightToLeft()
720 QFETCH(QString, text);
721 QFETCH(bool, emptyString);
722 QFETCH(bool, firstCharacter);
723 QFETCH(bool, lastCharacter);
724 QFETCH(bool, middleCharacter);
725 QFETCH(bool, startString);
726 QFETCH(bool, midString);
727 QFETCH(bool, endString);
729 QQuickTextInput textInput;
730 textInput.setText(text);
732 // first test that the right string is delivered to the QString::isRightToLeft()
733 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
734 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
735 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
736 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
737 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
738 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
740 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
741 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
743 // then test that the feature actually works
744 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
745 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
746 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
747 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
748 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
749 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
751 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
752 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
755 void tst_qquicktextinput::moveCursorSelection_data()
757 QTest::addColumn<QString>("testStr");
758 QTest::addColumn<int>("cursorPosition");
759 QTest::addColumn<int>("movePosition");
760 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
761 QTest::addColumn<int>("selectionStart");
762 QTest::addColumn<int>("selectionEnd");
763 QTest::addColumn<bool>("reversible");
765 // () contains the text selected by the cursor.
766 // <> contains the actual selection.
768 QTest::newRow("(t)he|characters")
769 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
770 QTest::newRow("do(g)|characters")
771 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
772 QTest::newRow("jum(p)ed|characters")
773 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
774 QTest::newRow("jumped( )over|characters")
775 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
776 QTest::newRow("(the )|characters")
777 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
778 QTest::newRow("( dog)|characters")
779 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
780 QTest::newRow("( jumped )|characters")
781 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
782 QTest::newRow("th(e qu)ick|characters")
783 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
784 QTest::newRow("la(zy d)og|characters")
785 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
786 QTest::newRow("jum(ped ov)er|characters")
787 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
788 QTest::newRow("()the|characters")
789 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
790 QTest::newRow("dog()|characters")
791 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
792 QTest::newRow("jum()ped|characters")
793 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
795 QTest::newRow("<(t)he>|words")
796 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
797 QTest::newRow("<do(g)>|words")
798 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
799 QTest::newRow("<jum(p)ed>|words")
800 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
801 QTest::newRow("<jumped( )>over|words,ltr")
802 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
803 QTest::newRow("jumped<( )over>|words,rtl")
804 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
805 QTest::newRow("<(the )>quick|words,ltr")
806 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
807 QTest::newRow("<(the )quick>|words,rtl")
808 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
809 QTest::newRow("<lazy( dog)>|words,ltr")
810 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
811 QTest::newRow("lazy<( dog)>|words,rtl")
812 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
813 QTest::newRow("<fox( jumped )>over|words,ltr")
814 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
815 QTest::newRow("fox<( jumped )over>|words,rtl")
816 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
817 QTest::newRow("<th(e qu)ick>|words")
818 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
819 QTest::newRow("<la(zy d)og|words>")
820 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
821 QTest::newRow("<jum(ped ov)er>|words")
822 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
823 QTest::newRow("<()>the|words")
824 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
825 QTest::newRow("dog<()>|words")
826 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
827 QTest::newRow("jum<()>ped|words")
828 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
830 QTest::newRow("Hello<(,)> |words")
831 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
832 QTest::newRow("Hello<(, )>world|words,ltr")
833 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
834 QTest::newRow("Hello<(, )world>|words,rtl")
835 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
836 QTest::newRow("<Hel(lo, )>world|words,ltr")
837 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
838 QTest::newRow("<Hel(lo, )world>|words,rtl")
839 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
840 QTest::newRow("<Hel(lo)>,|words")
841 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
842 QTest::newRow("Hello<()>,|words")
843 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
844 QTest::newRow("Hello,<()>|words")
845 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
846 QTest::newRow("Hello<,( )>world|words,ltr")
847 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
848 QTest::newRow("Hello,<( )world>|words,rtl")
849 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
850 QTest::newRow("Hello<,( world)>|words,ltr")
851 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
852 QTest::newRow("Hello,<( world)>|words,rtl")
853 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
854 QTest::newRow("Hello<,( world!)>|words,ltr")
855 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
856 QTest::newRow("Hello,<( world!)>|words,rtl")
857 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
858 QTest::newRow("Hello<(, world!)>|words")
859 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
860 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
862 // QTest::newRow("world<(!)>|words")
863 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
864 QTest::newRow("world!<()>)|words")
865 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
866 QTest::newRow("world<()>!)|words")
867 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
869 QTest::newRow("<(,)>olleH |words")
870 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
871 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
872 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
873 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
874 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
875 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
876 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
877 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
878 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
879 QTest::newRow(",<(ol)leH>,|words")
880 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
881 QTest::newRow(",<()>olleH|words")
882 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
883 QTest::newRow("<()>,olleH|words")
884 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
885 QTest::newRow("<dlrow( )>,olleH|words,ltr")
886 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
887 QTest::newRow("dlrow<( ),>olleH|words,rtl")
888 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
889 QTest::newRow("<(dlrow )>,olleH|words,ltr")
890 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
891 QTest::newRow("<(dlrow ),>olleH|words,rtl")
892 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
893 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
894 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
895 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
896 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
897 QTest::newRow("(!dlrow ,)olleH|words")
898 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
899 QTest::newRow("<(!)>dlrow|words")
900 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
901 QTest::newRow("<()>!dlrow|words")
902 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
903 QTest::newRow("!<()>dlrow|words")
904 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
906 QTest::newRow(" <s(pac)ey> text |words")
907 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
908 QTest::newRow(" spacey <t(ex)t> |words")
909 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
910 QTest::newRow("<( )>spacey text |words|ltr")
911 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
912 QTest::newRow("<( )spacey> text |words|rtl")
913 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
914 QTest::newRow("spacey <text( )>|words|ltr")
915 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
917 // QTest::newRow("spacey text<( )>|words|rtl")
918 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
919 QTest::newRow("<()> spacey text |words")
920 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
921 QTest::newRow(" spacey text <()>|words")
922 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
925 void tst_qquicktextinput::moveCursorSelection()
927 QFETCH(QString, testStr);
928 QFETCH(int, cursorPosition);
929 QFETCH(int, movePosition);
930 QFETCH(QQuickTextInput::SelectionMode, mode);
931 QFETCH(int, selectionStart);
932 QFETCH(int, selectionEnd);
933 QFETCH(bool, reversible);
935 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
936 QQmlComponent textinputComponent(&engine);
937 textinputComponent.setData(componentStr.toLatin1(), QUrl());
938 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
939 QVERIFY(textinputObject != 0);
941 textinputObject->setCursorPosition(cursorPosition);
942 textinputObject->moveCursorSelection(movePosition, mode);
944 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
945 QCOMPARE(textinputObject->selectionStart(), selectionStart);
946 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
949 textinputObject->setCursorPosition(movePosition);
950 textinputObject->moveCursorSelection(cursorPosition, mode);
952 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
953 QCOMPARE(textinputObject->selectionStart(), selectionStart);
954 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
957 delete textinputObject;
960 void tst_qquicktextinput::moveCursorSelectionSequence_data()
962 QTest::addColumn<QString>("testStr");
963 QTest::addColumn<int>("cursorPosition");
964 QTest::addColumn<int>("movePosition1");
965 QTest::addColumn<int>("movePosition2");
966 QTest::addColumn<int>("selection1Start");
967 QTest::addColumn<int>("selection1End");
968 QTest::addColumn<int>("selection2Start");
969 QTest::addColumn<int>("selection2End");
971 // () contains the text selected by the cursor.
972 // <> contains the actual selection.
973 // ^ is the revised cursor position.
974 // {} contains the revised selection.
976 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
981 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
986 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
991 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
996 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1001 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1006 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1011 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1016 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1021 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1026 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1031 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1036 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1041 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1046 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1051 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1056 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1061 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1067 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1072 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1078 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1083 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1089 QTest::newRow("{<(^} sp)acey> text |ltr")
1094 QTest::newRow("{<( ^}sp)acey> text |ltr")
1099 QTest::newRow("<( {s^p)acey>} text |rtl")
1104 QTest::newRow("<( {^sp)acey>} text |rtl")
1110 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1116 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1118 // << 15 << 12 << 14
1121 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1127 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
1129 // << 12 << 15 << 14
1134 void tst_qquicktextinput::moveCursorSelectionSequence()
1136 QFETCH(QString, testStr);
1137 QFETCH(int, cursorPosition);
1138 QFETCH(int, movePosition1);
1139 QFETCH(int, movePosition2);
1140 QFETCH(int, selection1Start);
1141 QFETCH(int, selection1End);
1142 QFETCH(int, selection2Start);
1143 QFETCH(int, selection2End);
1145 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1146 QQmlComponent textinputComponent(&engine);
1147 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1148 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1149 QVERIFY(textinputObject != 0);
1151 textinputObject->setCursorPosition(cursorPosition);
1153 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1154 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1155 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1156 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1158 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1159 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1160 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1161 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1163 delete textinputObject;
1166 void tst_qquicktextinput::dragMouseSelection()
1168 QString qmlfile = testFile("mouseselection_true.qml");
1170 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1173 canvas.requestActivateWindow();
1174 QTest::qWaitForWindowShown(&canvas);
1176 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1178 QVERIFY(canvas.rootObject() != 0);
1179 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1180 QVERIFY(textInputObject != 0);
1182 // press-and-drag-and-release from x1 to x2
1185 int y = textInputObject->height()/2;
1186 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1187 QTest::mouseMove(&canvas, QPoint(x2, y));
1188 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1191 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1192 QVERIFY(str1.length() > 3);
1194 // press and drag the current selection.
1197 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1198 QTest::mouseMove(&canvas, QPoint(x2, y));
1199 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1201 QString str2 = textInputObject->selectedText();
1202 QVERIFY(str2.length() > 3);
1204 QVERIFY(str1 != str2);
1207 void tst_qquicktextinput::mouseSelectionMode_data()
1209 QTest::addColumn<QString>("qmlfile");
1210 QTest::addColumn<bool>("selectWords");
1211 QTest::addColumn<bool>("focus");
1212 QTest::addColumn<bool>("focusOnPress");
1215 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1216 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1217 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1218 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1219 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1220 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1221 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1222 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1223 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1226 void tst_qquicktextinput::mouseSelectionMode()
1228 QFETCH(QString, qmlfile);
1229 QFETCH(bool, selectWords);
1230 QFETCH(bool, focus);
1231 QFETCH(bool, focusOnPress);
1233 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1235 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1238 canvas.requestActivateWindow();
1239 QTest::qWaitForWindowShown(&canvas);
1240 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1242 QVERIFY(canvas.rootObject() != 0);
1243 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1244 QVERIFY(textInputObject != 0);
1246 textInputObject->setFocus(focus);
1247 textInputObject->setFocusOnPress(focusOnPress);
1249 // press-and-drag-and-release from x1 to x2
1252 int y = textInputObject->height()/2;
1253 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1254 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1255 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1258 QTRY_COMPARE(textInputObject->selectedText(), text);
1260 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1261 QVERIFY(textInputObject->selectedText() != text);
1265 void tst_qquicktextinput::horizontalAlignment_data()
1267 QTest::addColumn<int>("hAlign");
1268 QTest::addColumn<QString>("expectfile");
1270 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1271 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1272 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1275 void tst_qquicktextinput::horizontalAlignment()
1277 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1279 QFETCH(int, hAlign);
1280 QFETCH(QString, expectfile);
1282 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1285 canvas.requestActivateWindow();
1286 QTest::qWaitForWindowShown(&canvas);
1287 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1288 QObject *ob = canvas.rootObject();
1290 ob->setProperty("horizontalAlignment",hAlign);
1291 QImage actual = canvas.grabFrameBuffer();
1293 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1295 QImage expect(expectfile);
1297 QCOMPARE(actual,expect);
1300 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1302 PlatformInputContext platformInputContext;
1303 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1304 inputMethodPrivate->testContext = &platformInputContext;
1306 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1307 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1308 QVERIFY(textInput != 0);
1311 const QString rtlText = textInput->text();
1313 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1314 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1316 // implicit alignment should follow the reading direction of RTL text
1317 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1318 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1319 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1320 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1322 // explicitly left aligned
1323 textInput->setHAlign(QQuickTextInput::AlignLeft);
1324 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1325 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1326 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1328 // explicitly right aligned
1329 textInput->setHAlign(QQuickTextInput::AlignRight);
1330 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1331 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1332 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1333 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1335 // explicitly center aligned
1336 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1337 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1338 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1339 QVERIFY(textInput->boundingRect().left() > 0);
1340 QVERIFY(textInput->boundingRect().right() < textInput->width());
1342 // reseted alignment should go back to following the text reading direction
1343 textInput->resetHAlign();
1344 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1345 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1346 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1347 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1349 // mirror the text item
1350 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1352 // mirrored implicit alignment should continue to follow the reading direction of the text
1353 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1354 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1355 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1356 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1358 // explicitly right aligned behaves as left aligned
1359 textInput->setHAlign(QQuickTextInput::AlignRight);
1360 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1361 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1362 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1364 // mirrored explicitly left aligned behaves as right aligned
1365 textInput->setHAlign(QQuickTextInput::AlignLeft);
1366 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1367 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1368 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1369 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1371 // disable mirroring
1372 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1373 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1374 textInput->resetHAlign();
1376 // English text should be implicitly left aligned
1377 textInput->setText("Hello world!");
1378 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1379 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1381 canvas.requestActivateWindow();
1382 QTest::qWaitForWindowShown(&canvas);
1383 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1385 // If there is no commited text, the preedit text should determine the alignment.
1386 textInput->setText(QString());
1387 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1388 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1389 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1390 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1392 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1393 // redundant as an actual input method may take care of it.
1394 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1396 // empty text with implicit alignment follows the system locale-based
1397 // keyboard input direction from QInputMethod::inputDirection()
1398 textInput->setText("");
1399 platformInputContext.setInputDirection(Qt::LeftToRight);
1400 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1401 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1402 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1404 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1405 platformInputContext.setInputDirection(Qt::RightToLeft);
1406 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1407 QCOMPARE(cursorRectangleSpy.count(), 1);
1408 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1409 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1410 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1412 // set input direction while having content
1413 platformInputContext.setInputDirection(Qt::LeftToRight);
1414 textInput->setText("a");
1415 platformInputContext.setInputDirection(Qt::RightToLeft);
1416 QTest::keyClick(&canvas, Qt::Key_Backspace);
1417 QVERIFY(textInput->text().isEmpty());
1418 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1419 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1420 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1422 // input direction changed while not having focus
1423 platformInputContext.setInputDirection(Qt::LeftToRight);
1424 textInput->setFocus(false);
1425 platformInputContext.setInputDirection(Qt::RightToLeft);
1426 textInput->setFocus(true);
1427 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1428 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1429 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1431 textInput->setHAlign(QQuickTextInput::AlignRight);
1432 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1433 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1434 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1436 // neutral text should fall back to input direction
1437 textInput->setFocus(true);
1438 textInput->resetHAlign();
1439 textInput->setText(" ()((=<>");
1440 platformInputContext.setInputDirection(Qt::LeftToRight);
1441 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1442 platformInputContext.setInputDirection(Qt::RightToLeft);
1443 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1446 void tst_qquicktextinput::verticalAlignment()
1448 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1449 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1450 QVERIFY(textInput != 0);
1453 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1454 QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1455 QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
1456 QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
1459 textInput->setVAlign(QQuickTextInput::AlignBottom);
1460 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1461 QVERIFY(textInput->boundingRect().top() > canvas.height() / 2);
1462 QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
1463 QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
1465 // explicitly center aligned
1466 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1467 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1468 QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1469 QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1470 QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
1471 QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
1472 QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
1473 QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
1476 void tst_qquicktextinput::clipRect()
1478 QQmlComponent component(&engine);
1479 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1480 QScopedPointer<QObject> object(component.create());
1481 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1484 QCOMPARE(input->clipRect().x(), qreal(0));
1485 QCOMPARE(input->clipRect().y(), qreal(0));
1486 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1487 QCOMPARE(input->clipRect().height(), input->height());
1489 input->setText("Hello World");
1490 QCOMPARE(input->clipRect().x(), qreal(0));
1491 QCOMPARE(input->clipRect().y(), qreal(0));
1492 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1493 QCOMPARE(input->clipRect().height(), input->height());
1495 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1496 input->setWidth(input->width() / 2);
1497 QCOMPARE(input->clipRect().x(), qreal(0));
1498 QCOMPARE(input->clipRect().y(), qreal(0));
1499 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1500 QCOMPARE(input->clipRect().height(), input->height());
1502 input->setHeight(input->height() * 2);
1503 QCOMPARE(input->clipRect().x(), qreal(0));
1504 QCOMPARE(input->clipRect().y(), qreal(0));
1505 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1506 QCOMPARE(input->clipRect().height(), input->height());
1508 QQmlComponent cursorComponent(&engine);
1509 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1511 input->setCursorDelegate(&cursorComponent);
1512 input->setCursorVisible(true);
1514 // If a cursor delegate is used it's size should determine the excess width.
1515 QCOMPARE(input->clipRect().x(), qreal(0));
1516 QCOMPARE(input->clipRect().y(), qreal(0));
1517 QCOMPARE(input->clipRect().width(), input->width() + 8);
1518 QCOMPARE(input->clipRect().height(), input->height());
1520 // Alignment, auto scroll, wrapping all don't affect the clip rect.
1521 input->setAutoScroll(false);
1522 QCOMPARE(input->clipRect().x(), qreal(0));
1523 QCOMPARE(input->clipRect().y(), qreal(0));
1524 QCOMPARE(input->clipRect().width(), input->width() + 8);
1525 QCOMPARE(input->clipRect().height(), input->height());
1527 input->setHAlign(QQuickTextInput::AlignRight);
1528 QCOMPARE(input->clipRect().x(), qreal(0));
1529 QCOMPARE(input->clipRect().y(), qreal(0));
1530 QCOMPARE(input->clipRect().width(), input->width() + 8);
1531 QCOMPARE(input->clipRect().height(), input->height());
1533 input->setWrapMode(QQuickTextInput::Wrap);
1534 QCOMPARE(input->clipRect().x(), qreal(0));
1535 QCOMPARE(input->clipRect().y(), qreal(0));
1536 QCOMPARE(input->clipRect().width(), input->width() + 8);
1537 QCOMPARE(input->clipRect().height(), input->height());
1539 input->setVAlign(QQuickTextInput::AlignBottom);
1540 QCOMPARE(input->clipRect().x(), qreal(0));
1541 QCOMPARE(input->clipRect().y(), qreal(0));
1542 QCOMPARE(input->clipRect().width(), input->width() + 8);
1543 QCOMPARE(input->clipRect().height(), input->height());
1546 void tst_qquicktextinput::boundingRect()
1548 QQmlComponent component(&engine);
1549 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1550 QScopedPointer<QObject> object(component.create());
1551 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1555 layout.setFont(input->font());
1557 if (!qmlDisableDistanceField()) {
1559 option.setUseDesignMetrics(true);
1560 layout.setTextOption(option);
1562 layout.beginLayout();
1563 QTextLine line = layout.createLine();
1566 QCOMPARE(input->boundingRect().x(), qreal(0));
1567 QCOMPARE(input->boundingRect().y(), qreal(0));
1568 QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1569 QCOMPARE(input->boundingRect().height(), line.height());
1571 input->setText("Hello World");
1573 layout.setText(input->text());
1574 layout.beginLayout();
1575 line = layout.createLine();
1578 QCOMPARE(input->boundingRect().x(), qreal(0));
1579 QCOMPARE(input->boundingRect().y(), qreal(0));
1580 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1581 QCOMPARE(input->boundingRect().height(), line.height());
1583 // the size of the bounding rect shouldn't be bounded by the size of item.
1584 input->setWidth(input->width() / 2);
1585 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1586 QCOMPARE(input->boundingRect().y(), qreal(0));
1587 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1588 QCOMPARE(input->boundingRect().height(), line.height());
1590 input->setHeight(input->height() * 2);
1591 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1592 QCOMPARE(input->boundingRect().y(), qreal(0));
1593 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1594 QCOMPARE(input->boundingRect().height(), line.height());
1596 QQmlComponent cursorComponent(&engine);
1597 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1599 input->setCursorDelegate(&cursorComponent);
1600 input->setCursorVisible(true);
1602 // Don't include the size of a cursor delegate as it has its own bounding rect.
1603 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1604 QCOMPARE(input->boundingRect().y(), qreal(0));
1605 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1606 QCOMPARE(input->boundingRect().height(), line.height());
1608 // Bounding rect left aligned when auto scroll is disabled;
1609 input->setAutoScroll(false);
1610 QCOMPARE(input->boundingRect().x(), qreal(0));
1611 QCOMPARE(input->boundingRect().y(), qreal(0));
1612 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1613 QCOMPARE(input->boundingRect().height(), line.height());
1615 input->setHAlign(QQuickTextInput::AlignRight);
1616 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1617 QCOMPARE(input->boundingRect().y(), qreal(0));
1618 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1619 QCOMPARE(input->boundingRect().height(), line.height());
1621 input->setWrapMode(QQuickTextInput::Wrap);
1622 QCOMPARE(input->boundingRect().right(), input->width());
1623 QCOMPARE(input->boundingRect().y(), qreal(0));
1624 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1625 QVERIFY(input->boundingRect().height() > line.height());
1627 input->setVAlign(QQuickTextInput::AlignBottom);
1628 QCOMPARE(input->boundingRect().right(), input->width());
1629 QCOMPARE(input->boundingRect().bottom(), input->height());
1630 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1631 QVERIFY(input->boundingRect().height() > line.height());
1634 void tst_qquicktextinput::positionAt()
1636 QQuickView canvas(testFileUrl("positionAt.qml"));
1637 QVERIFY(canvas.rootObject() != 0);
1639 canvas.requestActivateWindow();
1640 QTest::qWaitForWindowShown(&canvas);
1642 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1643 QVERIFY(textinputObject != 0);
1645 // Check autoscrolled...
1647 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1649 QTextLayout layout(textinputObject->text());
1650 layout.setFont(textinputObject->font());
1652 if (!qmlDisableDistanceField()) {
1654 option.setUseDesignMetrics(true);
1655 layout.setTextOption(option);
1657 layout.beginLayout();
1658 QTextLine line = layout.createLine();
1661 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1662 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1663 int textWidth = floor(line.horizontalAdvance());
1665 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1666 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1668 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1669 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1670 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1672 // Check without autoscroll...
1673 textinputObject->setAutoScroll(false);
1674 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1676 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1677 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1679 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1680 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1682 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1683 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1684 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1686 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1687 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1689 QString preeditText = textinputObject->text().mid(0, pos);
1690 textinputObject->setText(textinputObject->text().mid(pos));
1691 textinputObject->setCursorPosition(0);
1693 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1694 QVERIFY(qGuiApp->focusObject());
1695 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1697 // Check all points within the preedit text return the same position.
1698 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1699 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1700 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1702 // Verify positioning returns to normal after the preedit text.
1703 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1704 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1706 { QInputMethodEvent inputEvent;
1707 QVERIFY(qGuiApp->focusObject());
1708 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1711 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1713 const qreal y0 = line.height() / 2;
1714 const qreal y1 = line.height() * 3 / 2;
1716 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1717 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1719 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1720 QVERIFY(newLinePos > pos);
1721 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1724 void tst_qquicktextinput::maxLength()
1726 QQuickView canvas(testFileUrl("maxLength.qml"));
1727 QVERIFY(canvas.rootObject() != 0);
1729 canvas.requestActivateWindow();
1730 QTest::qWaitForWindowShown(&canvas);
1732 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1733 QVERIFY(textinputObject != 0);
1734 QVERIFY(textinputObject->text().isEmpty());
1735 QVERIFY(textinputObject->maxLength() == 10);
1736 foreach (const QString &str, standard) {
1737 QVERIFY(textinputObject->text().length() <= 10);
1738 textinputObject->setText(str);
1739 QVERIFY(textinputObject->text().length() <= 10);
1742 textinputObject->setText("");
1743 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1744 for (int i=0; i<20; i++) {
1745 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1746 //simulateKey(&canvas, Qt::Key_A);
1747 QTest::keyPress(&canvas, Qt::Key_A);
1748 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1753 void tst_qquicktextinput::masks()
1755 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1756 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1757 QQuickView canvas(testFileUrl("masks.qml"));
1759 canvas.requestActivateWindow();
1760 QVERIFY(canvas.rootObject() != 0);
1761 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1762 QVERIFY(textinputObject != 0);
1763 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1764 QVERIFY(textinputObject->text().length() == 0);
1765 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1766 QCOMPARE(textinputObject->length(), 8);
1767 for (int i=0; i<10; i++) {
1768 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1769 QCOMPARE(textinputObject->length(), 8);
1770 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1771 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1772 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1773 //simulateKey(&canvas, Qt::Key_A);
1774 QTest::keyPress(&canvas, Qt::Key_A);
1775 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1780 void tst_qquicktextinput::validators()
1782 // Note that this test assumes that the validators are working properly
1783 // so you may need to run their tests first. All validators are checked
1784 // here to ensure that their exposure to QML is working.
1786 QLocale::setDefault(QLocale(QStringLiteral("C")));
1788 QQuickView canvas(testFileUrl("validators.qml"));
1790 canvas.requestActivateWindow();
1792 QVERIFY(canvas.rootObject() != 0);
1794 QLocale defaultLocale;
1795 QLocale enLocale("en");
1796 QLocale deLocale("de_DE");
1798 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1800 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1802 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1803 QVERIFY(intValidator);
1804 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1805 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1806 intValidator->setLocaleName(enLocale.name());
1807 QCOMPARE(intValidator->localeName(), enLocale.name());
1808 QCOMPARE(intInput->validator()->locale(), enLocale);
1809 intValidator->resetLocaleName();
1810 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1811 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1813 intInput->setFocus(true);
1814 QTRY_VERIFY(intInput->hasActiveFocus());
1815 QCOMPARE(intInput->hasAcceptableInput(), false);
1816 QCOMPARE(intInput->property("acceptable").toBool(), false);
1817 QTest::keyPress(&canvas, Qt::Key_1);
1818 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1820 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1821 QCOMPARE(intInput->hasAcceptableInput(), false);
1822 QCOMPARE(intInput->property("acceptable").toBool(), false);
1823 QCOMPARE(intSpy.count(), 0);
1824 QTest::keyPress(&canvas, Qt::Key_2);
1825 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1827 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1828 QCOMPARE(intInput->hasAcceptableInput(), false);
1829 QCOMPARE(intInput->property("acceptable").toBool(), false);
1830 QCOMPARE(intSpy.count(), 0);
1831 QTest::keyPress(&canvas, Qt::Key_Period);
1832 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1834 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1835 QCOMPARE(intInput->hasAcceptableInput(), false);
1836 QTest::keyPress(&canvas, Qt::Key_Comma);
1837 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1839 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1840 QCOMPARE(intInput->hasAcceptableInput(), false);
1841 QTest::keyPress(&canvas, Qt::Key_Backspace);
1842 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1844 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1845 QCOMPARE(intInput->hasAcceptableInput(), false);
1846 intValidator->setLocaleName(deLocale.name());
1847 QTest::keyPress(&canvas, Qt::Key_Period);
1848 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1850 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1851 QCOMPARE(intInput->hasAcceptableInput(), false);
1852 QTest::keyPress(&canvas, Qt::Key_Backspace);
1853 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1855 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1856 QCOMPARE(intInput->hasAcceptableInput(), false);
1857 intValidator->resetLocaleName();
1858 QTest::keyPress(&canvas, Qt::Key_1);
1859 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1861 QCOMPARE(intInput->text(), QLatin1String("11"));
1862 QCOMPARE(intInput->hasAcceptableInput(), true);
1863 QCOMPARE(intInput->property("acceptable").toBool(), true);
1864 QCOMPARE(intSpy.count(), 1);
1865 QTest::keyPress(&canvas, Qt::Key_0);
1866 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1868 QCOMPARE(intInput->text(), QLatin1String("11"));
1869 QCOMPARE(intInput->hasAcceptableInput(), true);
1870 QCOMPARE(intInput->property("acceptable").toBool(), true);
1871 QCOMPARE(intSpy.count(), 1);
1873 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1875 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1877 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1878 QVERIFY(dblValidator);
1879 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1880 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1881 dblValidator->setLocaleName(enLocale.name());
1882 QCOMPARE(dblValidator->localeName(), enLocale.name());
1883 QCOMPARE(dblInput->validator()->locale(), enLocale);
1884 dblValidator->resetLocaleName();
1885 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1886 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1888 dblInput->setFocus(true);
1889 QVERIFY(dblInput->hasActiveFocus() == true);
1890 QCOMPARE(dblInput->hasAcceptableInput(), false);
1891 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1892 QTest::keyPress(&canvas, Qt::Key_1);
1893 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1895 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1896 QCOMPARE(dblInput->hasAcceptableInput(), false);
1897 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1898 QCOMPARE(dblSpy.count(), 0);
1899 QTest::keyPress(&canvas, Qt::Key_2);
1900 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1902 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1903 QCOMPARE(dblInput->hasAcceptableInput(), true);
1904 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1905 QCOMPARE(dblSpy.count(), 1);
1906 QTest::keyPress(&canvas, Qt::Key_Comma);
1907 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1909 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1910 QCOMPARE(dblInput->hasAcceptableInput(), true);
1911 QTest::keyPress(&canvas, Qt::Key_1);
1912 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1914 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1915 QCOMPARE(dblInput->hasAcceptableInput(), true);
1916 dblValidator->setLocaleName(deLocale.name());
1917 QCOMPARE(dblInput->hasAcceptableInput(), true);
1918 QTest::keyPress(&canvas, Qt::Key_1);
1919 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1921 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1922 QCOMPARE(dblInput->hasAcceptableInput(), true);
1923 QTest::keyPress(&canvas, Qt::Key_1);
1924 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1926 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1927 QCOMPARE(dblInput->hasAcceptableInput(), true);
1928 QTest::keyPress(&canvas, Qt::Key_Backspace);
1929 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1931 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1932 QCOMPARE(dblInput->hasAcceptableInput(), true);
1933 QTest::keyPress(&canvas, Qt::Key_Backspace);
1934 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1936 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1937 QCOMPARE(dblInput->hasAcceptableInput(), true);
1938 QTest::keyPress(&canvas, Qt::Key_Backspace);
1939 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1941 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1942 QCOMPARE(dblInput->hasAcceptableInput(), true);
1943 dblValidator->resetLocaleName();
1944 QTest::keyPress(&canvas, Qt::Key_Period);
1945 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1947 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1948 QCOMPARE(dblInput->hasAcceptableInput(), true);
1949 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1950 QCOMPARE(dblSpy.count(), 1);
1951 QTest::keyPress(&canvas, Qt::Key_1);
1952 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1954 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1955 QCOMPARE(dblInput->hasAcceptableInput(), true);
1956 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1957 QCOMPARE(dblSpy.count(), 1);
1958 QTest::keyPress(&canvas, Qt::Key_1);
1959 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1961 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1962 QCOMPARE(dblInput->hasAcceptableInput(), true);
1963 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1964 QCOMPARE(dblSpy.count(), 1);
1965 QTest::keyPress(&canvas, Qt::Key_1);
1966 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1968 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1969 QCOMPARE(dblInput->hasAcceptableInput(), true);
1970 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1971 QCOMPARE(dblSpy.count(), 1);
1973 // Ensure the validator doesn't prevent characters being removed.
1974 dblInput->setValidator(intInput->validator());
1975 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1976 QCOMPARE(dblInput->hasAcceptableInput(), false);
1977 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1978 QCOMPARE(dblSpy.count(), 2);
1979 QTest::keyPress(&canvas, Qt::Key_Backspace);
1980 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1982 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1983 QCOMPARE(dblInput->hasAcceptableInput(), false);
1984 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1985 QCOMPARE(dblSpy.count(), 2);
1986 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1987 QTest::keyPress(&canvas, Qt::Key_1);
1988 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1990 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1991 QCOMPARE(dblInput->hasAcceptableInput(), false);
1992 QCOMPARE(dblSpy.count(), 2);
1993 QTest::keyPress(&canvas, Qt::Key_Backspace);
1994 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1996 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1997 QCOMPARE(dblInput->hasAcceptableInput(), false);
1998 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1999 QCOMPARE(dblSpy.count(), 2);
2000 QTest::keyPress(&canvas, Qt::Key_Backspace);
2001 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2003 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2004 QCOMPARE(dblInput->hasAcceptableInput(), false);
2005 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2006 QCOMPARE(dblSpy.count(), 2);
2007 QTest::keyPress(&canvas, Qt::Key_Backspace);
2008 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2010 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2011 QCOMPARE(dblInput->hasAcceptableInput(), false);
2012 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2013 QCOMPARE(dblSpy.count(), 2);
2014 QTest::keyPress(&canvas, Qt::Key_Backspace);
2015 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2017 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2018 QCOMPARE(dblInput->hasAcceptableInput(), false);
2019 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2020 QCOMPARE(dblSpy.count(), 2);
2021 QTest::keyPress(&canvas, Qt::Key_1);
2022 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2024 QCOMPARE(dblInput->text(), QLatin1String("11"));
2025 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2026 QCOMPARE(dblInput->hasAcceptableInput(), true);
2027 QCOMPARE(dblSpy.count(), 3);
2029 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
2031 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2032 strInput->setFocus(true);
2033 QVERIFY(strInput->hasActiveFocus() == true);
2034 QCOMPARE(strInput->hasAcceptableInput(), false);
2035 QCOMPARE(strInput->property("acceptable").toBool(), false);
2036 QTest::keyPress(&canvas, Qt::Key_1);
2037 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2039 QTRY_COMPARE(strInput->text(), QLatin1String(""));
2040 QCOMPARE(strInput->hasAcceptableInput(), false);
2041 QCOMPARE(strInput->property("acceptable").toBool(), false);
2042 QCOMPARE(strSpy.count(), 0);
2043 QTest::keyPress(&canvas, Qt::Key_A);
2044 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2046 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2047 QCOMPARE(strInput->hasAcceptableInput(), false);
2048 QCOMPARE(strInput->property("acceptable").toBool(), false);
2049 QCOMPARE(strSpy.count(), 0);
2050 QTest::keyPress(&canvas, Qt::Key_A);
2051 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2053 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2054 QCOMPARE(strInput->hasAcceptableInput(), true);
2055 QCOMPARE(strInput->property("acceptable").toBool(), true);
2056 QCOMPARE(strSpy.count(), 1);
2057 QTest::keyPress(&canvas, Qt::Key_A);
2058 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2060 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2061 QCOMPARE(strInput->hasAcceptableInput(), true);
2062 QCOMPARE(strInput->property("acceptable").toBool(), true);
2063 QCOMPARE(strSpy.count(), 1);
2064 QTest::keyPress(&canvas, Qt::Key_A);
2065 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2067 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2068 QCOMPARE(strInput->hasAcceptableInput(), true);
2069 QCOMPARE(strInput->property("acceptable").toBool(), true);
2070 QCOMPARE(strSpy.count(), 1);
2071 QTest::keyPress(&canvas, Qt::Key_A);
2072 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2074 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2075 QCOMPARE(strInput->hasAcceptableInput(), true);
2076 QCOMPARE(strInput->property("acceptable").toBool(), true);
2077 QCOMPARE(strSpy.count(), 1);
2079 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
2080 QVERIFY(unvalidatedInput);
2081 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2082 unvalidatedInput->setFocus(true);
2083 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2084 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2085 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2086 QTest::keyPress(&canvas, Qt::Key_1);
2087 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2089 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2090 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2091 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2092 QCOMPARE(unvalidatedSpy.count(), 0);
2093 QTest::keyPress(&canvas, Qt::Key_A);
2094 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2096 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2097 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2098 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2099 QCOMPARE(unvalidatedSpy.count(), 0);
2102 void tst_qquicktextinput::inputMethods()
2104 QQuickView canvas(testFileUrl("inputmethods.qml"));
2106 canvas.requestActivateWindow();
2107 QTest::qWaitForWindowShown(&canvas);
2109 // test input method hints
2110 QVERIFY(canvas.rootObject() != 0);
2111 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
2112 QVERIFY(input != 0);
2113 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2114 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2115 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2116 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2117 QCOMPARE(inputMethodHintSpy.count(), 1);
2118 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2119 QCOMPARE(inputMethodHintSpy.count(), 1);
2122 QQuickTextInput plainInput;
2123 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2125 input->setFocus(true);
2126 QVERIFY(input->hasActiveFocus() == true);
2127 // test that input method event is committed
2128 QInputMethodEvent event;
2129 event.setCommitString( "My ", -12, 0);
2130 QTRY_COMPARE(qGuiApp->focusObject(), input);
2131 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2132 QCOMPARE(input->text(), QString("My Hello world!"));
2134 input->setCursorPosition(2);
2135 event.setCommitString("Your", -2, 2);
2136 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2137 QCOMPARE(input->text(), QString("Your Hello world!"));
2138 QCOMPARE(input->cursorPosition(), 4);
2140 input->setCursorPosition(7);
2141 event.setCommitString("Goodbye", -2, 5);
2142 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2143 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2144 QCOMPARE(input->cursorPosition(), 12);
2146 input->setCursorPosition(8);
2147 event.setCommitString("Our", -8, 4);
2148 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2149 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2150 QCOMPARE(input->cursorPosition(), 7);
2152 // input should reset selection even if replacement parameters are out of bounds
2153 input->setText("text");
2154 input->setCursorPosition(0);
2155 input->moveCursorSelection(input->text().length());
2156 event.setCommitString("replacement", -input->text().length(), input->text().length());
2157 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2158 QCOMPARE(input->selectionStart(), input->selectionEnd());
2160 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2161 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2162 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2164 input->setReadOnly(true);
2165 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2166 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2170 TextInput element should only handle left/right keys until the cursor reaches
2171 the extent of the text, then they should ignore the keys.
2174 void tst_qquicktextinput::navigation()
2176 QQuickView canvas(testFileUrl("navigation.qml"));
2178 canvas.requestActivateWindow();
2180 QVERIFY(canvas.rootObject() != 0);
2182 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2184 QVERIFY(input != 0);
2185 input->setCursorPosition(0);
2186 QTRY_VERIFY(input->hasActiveFocus() == true);
2187 simulateKey(&canvas, Qt::Key_Left);
2188 QVERIFY(input->hasActiveFocus() == false);
2189 simulateKey(&canvas, Qt::Key_Right);
2190 QVERIFY(input->hasActiveFocus() == true);
2191 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2192 input->setCursorPosition(input->text().length());
2193 input->select(0,input->text().length());
2194 QVERIFY(input->selectionStart() != input->selectionEnd());
2195 simulateKey(&canvas, Qt::Key_Right);
2196 QVERIFY(input->selectionStart() == input->selectionEnd());
2197 QVERIFY(input->selectionStart() == input->text().length());
2198 QVERIFY(input->hasActiveFocus() == true);
2199 simulateKey(&canvas, Qt::Key_Right);
2200 QVERIFY(input->hasActiveFocus() == false);
2201 simulateKey(&canvas, Qt::Key_Left);
2202 QVERIFY(input->hasActiveFocus() == true);
2204 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2205 input->setCursorPosition(2);
2206 QCOMPARE(input->cursorPosition(),2);
2207 simulateKey(&canvas, Qt::Key_Up);
2208 QCOMPARE(input->cursorPosition(),2);
2209 simulateKey(&canvas, Qt::Key_Down);
2210 QCOMPARE(input->cursorPosition(),2);
2212 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2213 input->setText(QString());
2214 QCOMPARE(input->cursorPosition(), 0);
2215 simulateKey(&canvas, Qt::Key_Right);
2216 QCOMPARE(input->hasActiveFocus(), false);
2217 simulateKey(&canvas, Qt::Key_Left);
2218 QCOMPARE(input->hasActiveFocus(), true);
2219 simulateKey(&canvas, Qt::Key_Left);
2220 QCOMPARE(input->hasActiveFocus(), false);
2223 void tst_qquicktextinput::navigation_RTL()
2225 QQuickView canvas(testFileUrl("navigation.qml"));
2227 canvas.requestActivateWindow();
2229 QVERIFY(canvas.rootObject() != 0);
2231 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2233 QVERIFY(input != 0);
2234 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2235 input->setText(QString::fromUtf16(arabic_str, 11));
2237 input->setCursorPosition(0);
2238 QTRY_VERIFY(input->hasActiveFocus() == true);
2241 simulateKey(&canvas, Qt::Key_Right);
2242 QVERIFY(input->hasActiveFocus() == false);
2245 simulateKey(&canvas, Qt::Key_Left);
2246 QVERIFY(input->hasActiveFocus() == true);
2248 input->setCursorPosition(input->text().length());
2249 QVERIFY(input->hasActiveFocus() == true);
2252 simulateKey(&canvas, Qt::Key_Left);
2253 QVERIFY(input->hasActiveFocus() == false);
2256 simulateKey(&canvas, Qt::Key_Right);
2257 QVERIFY(input->hasActiveFocus() == true);
2260 void tst_qquicktextinput::copyAndPaste() {
2261 #ifndef QT_NO_CLIPBOARD
2265 PasteboardRef pasteboard;
2266 OSStatus status = PasteboardCreate(0, &pasteboard);
2267 if (status == noErr)
2268 CFRelease(pasteboard);
2270 QSKIP("This machine doesn't support the clipboard");
2274 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2275 QQmlComponent textInputComponent(&engine);
2276 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2277 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2278 QVERIFY(textInput != 0);
2281 QCOMPARE(textInput->text().length(), 12);
2282 textInput->select(0, textInput->text().length());
2284 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2285 QCOMPARE(textInput->selectedText().length(), 12);
2286 textInput->setCursorPosition(0);
2287 QVERIFY(textInput->canPaste());
2289 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2290 QCOMPARE(textInput->text().length(), 24);
2293 QVERIFY(textInput->canPaste());
2294 textInput->setReadOnly(true);
2295 QVERIFY(!textInput->canPaste());
2296 textInput->setReadOnly(false);
2297 QVERIFY(textInput->canPaste());
2300 textInput->setCursorPosition(0);
2301 textInput->selectWord();
2302 QCOMPARE(textInput->selectedText(), QString("Hello"));
2304 // select all and cut
2305 textInput->selectAll();
2307 QCOMPARE(textInput->text().length(), 0);
2309 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2310 QCOMPARE(textInput->text().length(), 24);
2312 // clear copy buffer
2313 QClipboard *clipboard = QGuiApplication::clipboard();
2316 QVERIFY(!textInput->canPaste());
2318 // test that copy functionality is disabled
2319 // when echo mode is set to hide text/password mode
2322 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2323 textInput->setEchoMode(echoMode);
2324 textInput->setText("My password");
2325 textInput->select(0, textInput->text().length());
2327 if (echoMode == QQuickTextInput::Normal) {
2328 QVERIFY(!clipboard->text().isEmpty());
2329 QCOMPARE(clipboard->text(), QString("My password"));
2332 QVERIFY(clipboard->text().isEmpty());
2341 void tst_qquicktextinput::copyAndPasteKeySequence() {
2342 #ifndef QT_NO_CLIPBOARD
2346 PasteboardRef pasteboard;
2347 OSStatus status = PasteboardCreate(0, &pasteboard);
2348 if (status == noErr)
2349 CFRelease(pasteboard);
2351 QSKIP("This machine doesn't support the clipboard");
2355 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2356 QQmlComponent textInputComponent(&engine);
2357 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2358 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2359 QVERIFY(textInput != 0);
2361 QQuickCanvas canvas;
2362 textInput->setParentItem(canvas.rootItem());
2364 canvas.requestActivateWindow();
2365 QTest::qWaitForWindowShown(&canvas);
2366 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2369 QVERIFY(textInput->hasActiveFocus());
2370 QCOMPARE(textInput->text().length(), 12);
2371 textInput->select(0, textInput->text().length());
2372 simulateKeys(&canvas, QKeySequence::Copy);
2373 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2374 QCOMPARE(textInput->selectedText().length(), 12);
2375 textInput->setCursorPosition(0);
2376 QVERIFY(textInput->canPaste());
2377 simulateKeys(&canvas, QKeySequence::Paste);
2378 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2379 QCOMPARE(textInput->text().length(), 24);
2381 // select all and cut
2382 simulateKeys(&canvas, QKeySequence::SelectAll);
2383 simulateKeys(&canvas, QKeySequence::Cut);
2384 QCOMPARE(textInput->text().length(), 0);
2385 simulateKeys(&canvas, QKeySequence::Paste);
2386 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2387 QCOMPARE(textInput->text().length(), 24);
2389 // clear copy buffer
2390 QClipboard *clipboard = QGuiApplication::clipboard();
2393 QVERIFY(!textInput->canPaste());
2395 // test that copy functionality is disabled
2396 // when echo mode is set to hide text/password mode
2399 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2400 textInput->setEchoMode(echoMode);
2401 textInput->setText("My password");
2402 textInput->select(0, textInput->text().length());
2403 simulateKeys(&canvas, QKeySequence::Copy);
2404 if (echoMode == QQuickTextInput::Normal) {
2405 QVERIFY(!clipboard->text().isEmpty());
2406 QCOMPARE(clipboard->text(), QString("My password"));
2409 QVERIFY(clipboard->text().isEmpty());
2418 void tst_qquicktextinput::canPasteEmpty() {
2419 #ifndef QT_NO_CLIPBOARD
2421 QGuiApplication::clipboard()->clear();
2423 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2424 QQmlComponent textInputComponent(&engine);
2425 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2426 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2427 QVERIFY(textInput != 0);
2429 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2430 QCOMPARE(textInput->canPaste(), cp);
2435 void tst_qquicktextinput::canPaste() {
2436 #ifndef QT_NO_CLIPBOARD
2438 QGuiApplication::clipboard()->setText("Some text");
2440 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2441 QQmlComponent textInputComponent(&engine);
2442 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2443 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2444 QVERIFY(textInput != 0);
2446 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2447 QCOMPARE(textInput->canPaste(), cp);
2452 void tst_qquicktextinput::passwordCharacter()
2454 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2455 QQmlComponent textInputComponent(&engine);
2456 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2457 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2458 QVERIFY(textInput != 0);
2460 textInput->setPasswordCharacter("X");
2461 qreal implicitWidth = textInput->implicitWidth();
2462 textInput->setPasswordCharacter(".");
2464 // QTBUG-12383 content is updated and redrawn
2465 QVERIFY(textInput->implicitWidth() < implicitWidth);
2470 void tst_qquicktextinput::cursorDelegate_data()
2472 QTest::addColumn<QUrl>("source");
2473 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2474 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2475 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2478 void tst_qquicktextinput::cursorDelegate()
2480 QFETCH(QUrl, source);
2481 QQuickView view(source);
2483 view.requestActivateWindow();
2484 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2485 QVERIFY(textInputObject != 0);
2486 // Delegate is created on demand, and so won't be available immediately. Focus in or
2487 // setCursorVisible(true) will trigger creation.
2488 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2489 QVERIFY(!textInputObject->isCursorVisible());
2490 //Test Delegate gets created
2491 textInputObject->setFocus(true);
2492 QVERIFY(textInputObject->isCursorVisible());
2493 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2494 QVERIFY(delegateObject);
2495 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2496 //Test Delegate gets moved
2497 for (int i=0; i<= textInputObject->text().length(); i++) {
2498 textInputObject->setCursorPosition(i);
2499 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2500 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2502 textInputObject->setCursorPosition(0);
2503 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2504 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2506 // Test delegate gets moved on mouse press.
2507 textInputObject->setSelectByMouse(true);
2508 textInputObject->setCursorPosition(0);
2509 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2510 QTest::qWait(400); //ensure this isn't treated as a double-click
2511 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2513 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2514 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2515 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2517 // Test delegate gets moved on mouse drag
2518 textInputObject->setCursorPosition(0);
2519 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2520 QTest::qWait(400); //ensure this isn't treated as a double-click
2521 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2522 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2523 QGuiApplication::sendEvent(&view, &mv);
2524 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2526 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2527 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2529 textInputObject->setReadOnly(true);
2530 textInputObject->setCursorPosition(0);
2531 QTest::qWait(400); //ensure this isn't treated as a double-click
2532 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2534 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2535 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2536 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2538 textInputObject->setCursorPosition(0);
2539 QTest::qWait(400); //ensure this isn't treated as a double-click
2540 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2542 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2543 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2544 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2546 textInputObject->setCursorPosition(0);
2547 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2548 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2550 // Delegate moved when text is entered
2551 textInputObject->setText(QString());
2552 for (int i = 0; i < 20; ++i) {
2553 QTest::keyClick(&view, Qt::Key_A);
2554 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2555 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2558 // Delegate moved when text is entered by im.
2559 textInputObject->setText(QString());
2560 for (int i = 0; i < 20; ++i) {
2561 QInputMethodEvent event;
2562 event.setCommitString("a");
2563 QGuiApplication::sendEvent(&view, &event);
2564 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2565 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2568 //Test Delegate gets deleted
2569 textInputObject->setCursorDelegate(0);
2570 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2573 void tst_qquicktextinput::cursorVisible()
2575 QQuickTextInput input;
2576 input.componentComplete();
2577 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2579 QQuickView view(testFileUrl("cursorVisible.qml"));
2581 view.requestActivateWindow();
2582 QTest::qWaitForWindowShown(&view);
2583 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2585 QCOMPARE(input.isCursorVisible(), false);
2587 input.setCursorVisible(true);
2588 QCOMPARE(input.isCursorVisible(), true);
2589 QCOMPARE(spy.count(), 1);
2591 input.setCursorVisible(false);
2592 QCOMPARE(input.isCursorVisible(), false);
2593 QCOMPARE(spy.count(), 2);
2595 input.setFocus(true);
2596 QCOMPARE(input.isCursorVisible(), false);
2597 QCOMPARE(spy.count(), 2);
2599 input.setParentItem(view.rootObject());
2600 QCOMPARE(input.isCursorVisible(), true);
2601 QCOMPARE(spy.count(), 3);
2603 input.setFocus(false);
2604 QCOMPARE(input.isCursorVisible(), false);
2605 QCOMPARE(spy.count(), 4);
2607 input.setFocus(true);
2608 QCOMPARE(input.isCursorVisible(), true);
2609 QCOMPARE(spy.count(), 5);
2611 QWindow alternateView;
2612 alternateView.show();
2613 alternateView.requestActivateWindow();
2614 QTest::qWaitForWindowShown(&alternateView);
2616 QCOMPARE(input.isCursorVisible(), false);
2617 QCOMPARE(spy.count(), 6);
2619 view.requestActivateWindow();
2620 QTest::qWaitForWindowShown(&view);
2621 QCOMPARE(input.isCursorVisible(), true);
2622 QCOMPARE(spy.count(), 7);
2624 { // Cursor attribute with 0 length hides cursor.
2625 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2626 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2627 QCoreApplication::sendEvent(&input, &ev);
2629 QCOMPARE(input.isCursorVisible(), false);
2630 QCOMPARE(spy.count(), 8);
2632 { // Cursor attribute with non zero length shows cursor.
2633 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2634 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2635 QCoreApplication::sendEvent(&input, &ev);
2637 QCOMPARE(input.isCursorVisible(), true);
2638 QCOMPARE(spy.count(), 9);
2640 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2641 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2642 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2643 QCoreApplication::sendEvent(&input, &ev);
2645 QCOMPARE(input.isCursorVisible(), false);
2646 QCOMPARE(spy.count(), 10);
2648 input.setText("something");
2649 QCOMPARE(input.isCursorVisible(), true);
2650 QCOMPARE(spy.count(), 11);
2652 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2653 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2654 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2655 QCoreApplication::sendEvent(&input, &ev);
2657 QCOMPARE(input.isCursorVisible(), false);
2658 QCOMPARE(spy.count(), 12);
2660 input.setCursorPosition(5);
2661 QCOMPARE(input.isCursorVisible(), true);
2662 QCOMPARE(spy.count(), 13);
2665 void tst_qquicktextinput::cursorRectangle_data()
2667 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2669 QTest::addColumn<QString>("text");
2670 QTest::addColumn<int>("positionAtWidth");
2671 QTest::addColumn<int>("wrapPosition");
2672 QTest::addColumn<QString>("shortText");
2673 QTest::addColumn<bool>("leftToRight");
2675 QTest::newRow("left to right")
2676 << "Hello World!" << 5 << 11
2679 QTest::newRow("right to left")
2680 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2681 << QString::fromUtf16(arabic_str, 3)
2685 void tst_qquicktextinput::cursorRectangle()
2688 QFETCH(QString, text);
2689 QFETCH(int, positionAtWidth);
2690 QFETCH(int, wrapPosition);
2691 QFETCH(QString, shortText);
2692 QFETCH(bool, leftToRight);
2694 QQuickTextInput input;
2695 input.setText(text);
2696 input.componentComplete();
2698 QTextLayout layout(text);
2699 layout.setFont(input.font());
2700 if (!qmlDisableDistanceField()) {
2702 option.setUseDesignMetrics(true);
2703 layout.setTextOption(option);
2705 layout.beginLayout();
2706 QTextLine line = layout.createLine();
2711 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2713 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2714 offset = line.horizontalAdvance() - input.width();
2716 input.setHeight(qCeil(line.height() * 3 / 2));
2720 for (int i = 0; i <= positionAtWidth; ++i) {
2721 input.setCursorPosition(i);
2722 r = input.cursorRectangle();
2724 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2725 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2726 QCOMPARE(input.positionToRectangle(i), r);
2729 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2730 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2732 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2733 input.setCursorPosition(i);
2734 QCOMPARE(r, input.cursorRectangle());
2735 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2736 QCOMPARE(input.positionToRectangle(i), r);
2739 for (int i = text.length() - 2; i >= 0; --i) {
2740 input.setCursorPosition(i);
2741 r = input.cursorRectangle();
2742 QCOMPARE(r.top(), 0.);
2744 QVERIFY(r.right() >= 0);
2746 QVERIFY(r.left() <= input.width());
2748 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2749 QCOMPARE(input.positionToRectangle(i), r);
2752 // Check position with word wrap.
2753 input.setWrapMode(QQuickTextInput::WordWrap);
2754 input.setAutoScroll(false);
2755 for (int i = 0; i < wrapPosition; ++i) {
2756 input.setCursorPosition(i);
2757 r = input.cursorRectangle();
2759 if (i > positionAtWidth)
2760 QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2761 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2762 QCOMPARE(r.top(), 0.);
2763 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2764 QCOMPARE(input.positionToRectangle(i), r);
2767 input.setCursorPosition(wrapPosition);
2768 r = input.cursorRectangle();
2770 QCOMPARE(r.left(), 0.);
2772 QCOMPARE(r.left(), input.width());
2774 QVERIFY(r.top() >= line.height() - 1);
2775 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2776 QCOMPARE(input.positionToRectangle(11), r);
2778 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2779 input.setCursorPosition(i);
2780 r = input.cursorRectangle();
2781 QVERIFY(r.top() >= line.height() - 1);
2782 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2783 QCOMPARE(input.positionToRectangle(i), r);
2786 // Check vertical scrolling with word wrap.
2787 input.setAutoScroll(true);
2788 for (int i = 0; i <= positionAtWidth; ++i) {
2789 input.setCursorPosition(i);
2790 r = input.cursorRectangle();
2792 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2793 QCOMPARE(r.top(), 0.);
2794 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2795 QCOMPARE(input.positionToRectangle(i), r);
2798 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
2799 // reaches the next non-whitespace character.
2800 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2801 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
2802 input.setCursorPosition(i);
2803 QCOMPARE(r, input.cursorRectangle());
2804 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2805 QCOMPARE(input.positionToRectangle(i), r);
2808 input.setCursorPosition(wrapPosition);
2809 r = input.cursorRectangle();
2811 QCOMPARE(r.left(), 0.);
2813 QCOMPARE(r.left(), input.width());
2815 QVERIFY(r.bottom() >= input.height());
2816 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2817 QCOMPARE(input.positionToRectangle(11), r);
2819 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2820 input.setCursorPosition(i);
2821 r = input.cursorRectangle();
2822 QVERIFY(r.bottom() >= input.height());
2823 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2824 QCOMPARE(input.positionToRectangle(i), r);
2827 for (int i = text.length() - 2; i >= wrapPosition; --i) {
2828 input.setCursorPosition(i);
2829 r = input.cursorRectangle();
2830 QVERIFY(r.bottom() >= input.height());
2831 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2832 QCOMPARE(input.positionToRectangle(i), r);
2835 input.setCursorPosition(wrapPosition - 1);
2836 r = input.cursorRectangle();
2837 QCOMPARE(r.top(), 0.);
2838 QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2839 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2840 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2841 QCOMPARE(input.positionToRectangle(10), r);
2843 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
2844 input.setCursorPosition(i);
2845 QCOMPARE(r, input.cursorRectangle());
2846 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2847 QCOMPARE(input.positionToRectangle(i), r);
2850 for (int i = positionAtWidth; i >= 0; --i) {
2851 input.setCursorPosition(i);
2852 r = input.cursorRectangle();
2853 QCOMPARE(r.top(), 0.);
2854 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2855 QCOMPARE(input.positionToRectangle(i), r);
2858 input.setText(shortText);
2859 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
2860 r = input.cursorRectangle();
2861 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2864 void tst_qquicktextinput::readOnly()
2866 QQuickView canvas(testFileUrl("readOnly.qml"));
2868 canvas.requestActivateWindow();
2870 QVERIFY(canvas.rootObject() != 0);
2872 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2874 QVERIFY(input != 0);
2875 QTRY_VERIFY(input->hasActiveFocus() == true);
2876 QVERIFY(input->isReadOnly() == true);
2877 QString initial = input->text();
2878 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2879 simulateKey(&canvas, k);
2880 simulateKey(&canvas, Qt::Key_Return);
2881 simulateKey(&canvas, Qt::Key_Space);
2882 simulateKey(&canvas, Qt::Key_Escape);
2883 QCOMPARE(input->text(), initial);
2885 input->setCursorPosition(3);
2886 input->setReadOnly(false);
2887 QCOMPARE(input->isReadOnly(), false);
2888 QCOMPARE(input->cursorPosition(), input->text().length());
2891 void tst_qquicktextinput::echoMode()
2893 QQuickView canvas(testFileUrl("echoMode.qml"));
2895 canvas.requestActivateWindow();
2896 QTest::qWaitForWindowShown(&canvas);
2897 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2899 QVERIFY(canvas.rootObject() != 0);
2901 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2903 QVERIFY(input != 0);
2904 QTRY_VERIFY(input->hasActiveFocus() == true);
2905 QString initial = input->text();
2906 Qt::InputMethodHints ref;
2907 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2908 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2909 QCOMPARE(input->displayText(), input->text());
2911 ref &= ~Qt::ImhHiddenText;
2912 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2913 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2914 input->setEchoMode(QQuickTextInput::NoEcho);
2915 QCOMPARE(input->text(), initial);
2916 QCOMPARE(input->displayText(), QLatin1String(""));
2917 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2919 ref |= Qt::ImhHiddenText;
2920 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2921 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2922 input->setEchoMode(QQuickTextInput::Password);
2924 ref |= Qt::ImhHiddenText;
2925 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2926 QCOMPARE(input->text(), initial);
2927 QCOMPARE(input->displayText(), QLatin1String("********"));
2928 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2929 // clearing input hints do not clear bits set by echo mode
2930 input->setInputMethodHints(Qt::ImhNone);
2931 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2932 input->setPasswordCharacter(QChar('Q'));
2933 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2934 QCOMPARE(input->text(), initial);
2935 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2936 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2937 //PasswordEchoOnEdit
2938 ref &= ~Qt::ImhHiddenText;
2939 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2940 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2941 QCOMPARE(input->text(), initial);
2942 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2943 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2944 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2945 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2946 QCOMPARE(input->text(), QLatin1String("a"));
2947 QCOMPARE(input->displayText(), QLatin1String("a"));
2948 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2949 input->setFocus(false);
2950 QVERIFY(input->hasActiveFocus() == false);
2951 QCOMPARE(input->displayText(), QLatin1String("Q"));
2952 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2953 input->setFocus(true);
2954 QVERIFY(input->hasActiveFocus());
2955 QInputMethodEvent inputEvent;
2956 inputEvent.setCommitString(initial);
2957 QGuiApplication::sendEvent(input, &inputEvent);
2958 QCOMPARE(input->text(), initial);
2959 QCOMPARE(input->displayText(), initial);
2960 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2963 void tst_qquicktextinput::passwordEchoDelay()
2965 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
2967 QSKIP("No mask delay in use");
2968 QQuickView canvas(testFileUrl("echoMode.qml"));
2970 canvas.requestActivateWindow();
2971 QTest::qWaitForWindowShown(&canvas);
2972 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2974 QVERIFY(canvas.rootObject() != 0);
2976 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2978 QChar fillChar = QLatin1Char('*');
2980 input->setEchoMode(QQuickTextInput::Password);
2981 QCOMPARE(input->displayText(), QString(8, fillChar));
2982 input->setText(QString());
2983 QCOMPARE(input->displayText(), QString());
2985 QTest::keyPress(&canvas, '0');
2986 QTest::keyPress(&canvas, '1');
2987 QTest::keyPress(&canvas, '2');
2988 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2989 QTest::keyPress(&canvas, '3');
2990 QTest::keyPress(&canvas, '4');
2991 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2992 QTest::keyPress(&canvas, Qt::Key_Backspace);
2993 QCOMPARE(input->displayText(), QString(4, fillChar));
2994 QTest::keyPress(&canvas, '4');
2995 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2996 QTest::qWait(maskDelay);
2997 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2998 QTest::keyPress(&canvas, '5');
2999 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3000 input->setFocus(false);
3001 QVERIFY(!input->hasFocus());
3002 QCOMPARE(input->displayText(), QString(6, fillChar));
3003 input->setFocus(true);
3004 QTRY_VERIFY(input->hasFocus());
3005 QCOMPARE(input->displayText(), QString(6, fillChar));
3006 QTest::keyPress(&canvas, '6');
3007 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3009 QInputMethodEvent ev;
3010 ev.setCommitString(QLatin1String("7"));
3011 QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev);
3012 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3014 input->setCursorPosition(3);
3015 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3016 QTest::keyPress(&canvas, 'a');
3017 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3018 QTest::keyPress(&canvas, Qt::Key_Backspace);
3019 QCOMPARE(input->displayText(), QString(8, fillChar));
3023 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3025 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3026 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3028 QGuiApplication::sendEvent(view, &press);
3029 QGuiApplication::sendEvent(view, &release);
3033 void tst_qquicktextinput::focusOnPress()
3035 QString componentStr =
3036 "import QtQuick 2.0\n"
3038 "property bool selectOnFocus: false\n"
3039 "width: 100; height: 50\n"
3040 "activeFocusOnPress: true\n"
3041 "text: \"Hello World\"\n"
3042 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3044 QQmlComponent texteditComponent(&engine);
3045 texteditComponent.setData(componentStr.toLatin1(), QUrl());
3046 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3047 QVERIFY(textInputObject != 0);
3048 QCOMPARE(textInputObject->focusOnPress(), true);
3049 QCOMPARE(textInputObject->hasFocus(), false);
3051 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3052 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3053 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3055 textInputObject->setFocusOnPress(true);
3056 QCOMPARE(textInputObject->focusOnPress(), true);
3057 QCOMPARE(activeFocusOnPressSpy.count(), 0);
3059 QQuickCanvas canvas;
3060 canvas.resize(100, 50);
3061 textInputObject->setParentItem(canvas.rootItem());
3063 canvas.requestActivateWindow();
3064 QTest::qWaitForWindowShown(&canvas);
3065 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3067 QCOMPARE(textInputObject->hasFocus(), false);
3068 QCOMPARE(textInputObject->hasActiveFocus(), false);
3070 QPoint centerPoint(canvas.width()/2, canvas.height()/2);
3071 Qt::KeyboardModifiers noModifiers = 0;
3072 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3073 QGuiApplication::processEvents();
3074 QCOMPARE(textInputObject->hasFocus(), true);
3075 QCOMPARE(textInputObject->hasActiveFocus(), true);
3076 QCOMPARE(focusSpy.count(), 1);
3077 QCOMPARE(activeFocusSpy.count(), 1);
3078 QCOMPARE(textInputObject->selectedText(), QString());
3079 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3081 textInputObject->setFocusOnPress(false);
3082 QCOMPARE(textInputObject->focusOnPress(), false);
3083 QCOMPARE(activeFocusOnPressSpy.count(), 1);
3085 textInputObject->setFocus(false);
3086 QCOMPARE(textInputObject->hasFocus(), false);
3087 QCOMPARE(textInputObject->hasActiveFocus(), false);
3088 QCOMPARE(focusSpy.count(), 2);
3089 QCOMPARE(activeFocusSpy.count(), 2);
3091 // Wait for double click timeout to expire before clicking again.
3093 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3094 QGuiApplication::processEvents();
3095 QCOMPARE(textInputObject->hasFocus(), false);
3096 QCOMPARE(textInputObject->hasActiveFocus(), false);
3097 QCOMPARE(focusSpy.count(), 2);
3098 QCOMPARE(activeFocusSpy.count(), 2);
3099 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3101 textInputObject->setFocusOnPress(true);
3102 QCOMPARE(textInputObject->focusOnPress(), true);
3103 QCOMPARE(activeFocusOnPressSpy.count(), 2);
3105 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3106 textInputObject->setProperty("selectOnFocus", true);
3109 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3110 QGuiApplication::processEvents();
3111 QCOMPARE(textInputObject->hasFocus(), true);
3112 QCOMPARE(textInputObject->hasActiveFocus(), true);
3113 QCOMPARE(focusSpy.count(), 3);
3114 QCOMPARE(activeFocusSpy.count(), 3);
3115 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3116 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3119 void tst_qquicktextinput::openInputPanel()
3121 PlatformInputContext platformInputContext;
3122 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3123 inputMethodPrivate->testContext = &platformInputContext;
3125 QQuickView view(testFileUrl("openInputPanel.qml"));
3127 view.requestActivateWindow();
3128 QTest::qWaitForWindowShown(&view);
3129 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3131 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3134 // check default values
3135 QVERIFY(input->focusOnPress());
3136 QVERIFY(!input->hasActiveFocus());
3137 QVERIFY(qApp->focusObject() != input);
3138 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3140 // input panel should open on focus
3141 QPoint centerPoint(view.width()/2, view.height()/2);
3142 Qt::KeyboardModifiers noModifiers = 0;
3143 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3144 QGuiApplication::processEvents();
3145 QVERIFY(input->hasActiveFocus());
3146 QCOMPARE(qApp->focusObject(), input);
3147 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3148 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3150 // input panel should be re-opened when pressing already focused TextInput
3151 qApp->inputMethod()->hide();
3152 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3153 QVERIFY(input->hasActiveFocus());
3154 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3155 QGuiApplication::processEvents();
3156 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3157 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3159 // input panel should stay visible if focus is lost to another text inputor
3160 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3161 QQuickTextInput anotherInput;
3162 anotherInput.componentComplete();
3163 anotherInput.setParentItem(view.rootObject());
3164 anotherInput.setFocus(true);
3165 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3166 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3167 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3169 anotherInput.setFocus(false);
3170 QVERIFY(qApp->focusObject() != &anotherInput);
3171 QCOMPARE(view.activeFocusItem(), view.rootItem());
3172 anotherInput.setFocus(true);
3174 qApp->inputMethod()->hide();
3176 // input panel should not be opened if TextInput is read only
3177 input->setReadOnly(true);
3178 input->setFocus(true);
3179 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3180 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3181 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3182 QGuiApplication::processEvents();
3183 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3185 // input panel should not be opened if focusOnPress is set to false
3186 input->setFocusOnPress(false);
3187 input->setFocus(false);
3188 input->setFocus(true);
3189 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3190 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3191 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3192 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3194 // input panel should open when openSoftwareInputPanel is called
3195 input->openSoftwareInputPanel();
3196 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3198 // input panel should close when closeSoftwareInputPanel is called
3199 input->closeSoftwareInputPanel();
3200 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3203 class MyTextInput : public QQuickTextInput
3206 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3210 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3213 return QQuickTextInput::updatePaintNode(node, data);
3218 void tst_qquicktextinput::setHAlignClearCache()
3222 input.setText("Hello world");
3223 input.setParentItem(view.rootItem());
3225 view.requestActivateWindow();
3226 QTest::qWaitForWindowShown(&view);
3228 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3230 QTRY_COMPARE(input.nbPaint, 1);
3231 input.setHAlign(QQuickTextInput::AlignRight);
3232 //Changing the alignment should trigger a repaint
3233 QTRY_COMPARE(input.nbPaint, 2);
3236 void tst_qquicktextinput::focusOutClearSelection()
3239 QQuickTextInput input;
3240 QQuickTextInput input2;
3241 input.setText(QLatin1String("Hello world"));
3242 input.setFocus(true);
3243 input2.setParentItem(view.rootItem());
3244 input.setParentItem(view.rootItem());
3245 input.componentComplete();
3246 input2.componentComplete();
3248 view.requestActivateWindow();
3249 QTest::qWaitForWindowShown(&view);
3251 //The selection should work
3252 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3253 input2.setFocus(true);
3254 QGuiApplication::processEvents();
3255 //The input lost the focus selection should be cleared
3256 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3259 void tst_qquicktextinput::geometrySignals()
3261 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3262 QObject *o = component.create();
3264 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3265 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3269 void tst_qquicktextinput::contentSize()
3271 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3272 QQmlComponent textComponent(&engine);
3273 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3274 QScopedPointer<QObject> object(textComponent.create());
3275 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3277 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3279 textObject->setText("The quick red fox jumped over the lazy brown dog");
3281 QVERIFY(textObject->contentWidth() > textObject->width());
3282 QVERIFY(textObject->contentHeight() < textObject->height());
3283 QCOMPARE(spy.count(), 1);
3285 textObject->setWrapMode(QQuickTextInput::WordWrap);
3286 QVERIFY(textObject->contentWidth() <= textObject->width());
3287 QVERIFY(textObject->contentHeight() > textObject->height());
3288 QCOMPARE(spy.count(), 2);
3290 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3292 QVERIFY(textObject->contentWidth() > textObject->width());
3293 QVERIFY(textObject->contentHeight() > textObject->height());
3294 QCOMPARE(spy.count(), 3);
3296 textObject->setText("The quick red fox jumped over the lazy brown dog");
3297 for (int w = 60; w < 120; ++w) {
3298 textObject->setWidth(w);
3299 QVERIFY(textObject->contentWidth() <= textObject->width());
3300 QVERIFY(textObject->contentHeight() > textObject->height());
3304 static void sendPreeditText(const QString &text, int cursor)
3306 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3307 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3308 QCoreApplication::sendEvent(qGuiApp->focusObject(), &event);
3311 void tst_qquicktextinput::preeditAutoScroll()
3313 QString preeditText = "califragisiticexpialidocious!";
3315 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3317 view.requestActivateWindow();
3318 QTest::qWaitForWindowShown(&view);
3319 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3320 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3322 QVERIFY(input->hasActiveFocus());
3324 input->setWidth(input->implicitWidth());
3326 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3327 int cursorRectangleChanges = 0;
3329 // test the text is scrolled so the preedit is visible.
3330 sendPreeditText(preeditText.mid(0, 3), 1);
3331 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3332 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3333 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3335 // test the text is scrolled back when the preedit is removed.
3336 QInputMethodEvent imEvent;
3337 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3338 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3339 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3340 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3342 QTextLayout layout(preeditText);
3343 layout.setFont(input->font());
3344 if (!qmlDisableDistanceField()) {
3346 option.setUseDesignMetrics(true);
3347 layout.setTextOption(option);
3349 layout.beginLayout();
3350 QTextLine line = layout.createLine();
3353 // test if the preedit is larger than the text input that the
3354 // character preceding the cursor is still visible.
3355 qreal x = input->positionToRectangle(0).x();
3356 for (int i = 0; i < 3; ++i) {
3357 sendPreeditText(preeditText, i + 1);
3358 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3359 QVERIFY(input->cursorRectangle().right() >= width - 3);
3360 QVERIFY(input->positionToRectangle(0).x() < x);
3361 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3362 x = input->positionToRectangle(0).x();
3364 for (int i = 1; i >= 0; --i) {
3365 sendPreeditText(preeditText, i + 1);
3366 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3367 QVERIFY(input->cursorRectangle().right() >= width - 3);
3368 QVERIFY(input->positionToRectangle(0).x() > x);
3369 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3370 x = input->positionToRectangle(0).x();
3373 // Test incrementing the preedit cursor doesn't cause further
3374 // scrolling when right most text is visible.
3375 sendPreeditText(preeditText, preeditText.length() - 3);
3376 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3377 x = input->positionToRectangle(0).x();
3378 for (int i = 2; i >= 0; --i) {
3379 sendPreeditText(preeditText, preeditText.length() - i);
3380 QCOMPARE(input->positionToRectangle(0).x(), x);
3381 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3383 for (int i = 1; i < 3; ++i) {
3384 sendPreeditText(preeditText, preeditText.length() - i);
3385 QCOMPARE(input->positionToRectangle(0).x(), x);
3386 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3389 // Test disabling auto scroll.
3390 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3392 input->setAutoScroll(false);
3393 sendPreeditText(preeditText.mid(0, 3), 1);
3394 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3395 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3398 void tst_qquicktextinput::preeditCursorRectangle()
3400 QString preeditText = "super";
3402 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3404 view.requestActivateWindow();
3405 QTest::qWaitForWindowShown(&view);
3406 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3407 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3410 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3415 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3416 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3417 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3419 // Verify that the micro focus rect is positioned the same for position 0 as
3420 // it would be if there was no preedit text.
3421 sendPreeditText(preeditText, 0);
3422 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3423 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3424 QCOMPARE(currentRect, previousRect);
3425 QCOMPARE(input->cursorRectangle(), currentRect);
3426 QCOMPARE(cursor->pos(), currentRect.topLeft());
3428 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3429 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3431 // Verify that the micro focus rect moves to the left as the cursor position
3433 for (int i = 1; i <= 5; ++i) {
3434 sendPreeditText(preeditText, i);
3435 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3436 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3437 QVERIFY(previousRect.left() < currentRect.left());
3438 QCOMPARE(input->cursorRectangle(), currentRect);
3439 QCOMPARE(cursor->pos(), currentRect.topLeft());
3440 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3441 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3442 previousRect = currentRect;
3445 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3446 // but the (non-zero) cursor position is the same.
3449 sendPreeditText("wwwww", 5);
3450 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3451 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3452 QCOMPARE(input->cursorRectangle(), currentRect);
3453 QCOMPARE(cursor->pos(), currentRect.topLeft());
3454 QCOMPARE(inputSpy.count(), 1);
3455 QCOMPARE(panelSpy.count(), 1);
3457 // Verify that if there is no preedit cursor then the micro focus rect is the
3458 // same as it would be if it were positioned at the end of the preedit text.
3461 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3462 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
3463 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3464 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3465 QCOMPARE(currentRect, previousRect);
3466 QCOMPARE(input->cursorRectangle(), currentRect);
3467 QCOMPARE(cursor->pos(), currentRect.topLeft());
3468 QCOMPARE(inputSpy.count(), 1);
3469 QCOMPARE(panelSpy.count(), 1);
3472 void tst_qquicktextinput::inputContextMouseHandler()
3474 PlatformInputContext platformInputContext;
3475 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3476 inputMethodPrivate->testContext = &platformInputContext;
3478 QString text = "supercalifragisiticexpialidocious!";
3479 QQuickView view(testFileUrl("inputContext.qml"));
3480 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3483 input->setFocus(true);
3487 view.requestActivateWindow();
3488 QTest::qWaitForWindowShown(&view);
3489 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3491 QTextLayout layout(text);
3492 layout.setFont(input->font());
3493 if (!qmlDisableDistanceField()) {
3495 option.setUseDesignMetrics(true);
3496 layout.setTextOption(option);
3498 layout.beginLayout();
3499 QTextLine line = layout.createLine();
3502 const qreal x = line.cursorToX(2, QTextLine::Leading);
3503 const qreal y = line.height() / 2;
3504 QPoint position = QPointF(x, y).toPoint();
3506 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3507 QGuiApplication::sendEvent(input, &inputEvent);
3509 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3510 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3511 QGuiApplication::processEvents();
3513 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3514 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3515 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3518 void tst_qquicktextinput::inputMethodComposing()
3520 QString text = "supercalifragisiticexpialidocious!";
3522 QQuickView view(testFileUrl("inputContext.qml"));
3524 view.requestActivateWindow();
3525 QTest::qWaitForWindowShown(&view);
3526 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3527 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3529 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3531 QCOMPARE(input->isInputMethodComposing(), false);
3533 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3534 QGuiApplication::sendEvent(input, &event);
3536 QCOMPARE(input->isInputMethodComposing(), true);
3537 QCOMPARE(spy.count(), 1);
3540 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3541 QGuiApplication::sendEvent(input, &event);
3543 QCOMPARE(spy.count(), 1);
3546 QInputMethodEvent event;
3547 QGuiApplication::sendEvent(input, &event);
3549 QCOMPARE(input->isInputMethodComposing(), false);
3550 QCOMPARE(spy.count(), 2);
3552 // Changing the text while not composing doesn't alter the composing state.
3553 input->setText(text.mid(0, 16));
3554 QCOMPARE(input->isInputMethodComposing(), false);
3555 QCOMPARE(spy.count(), 2);
3558 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3559 QGuiApplication::sendEvent(input, &event);
3561 QCOMPARE(input->isInputMethodComposing(), true);
3562 QCOMPARE(spy.count(), 3);
3564 // Changing the text while composing cancels composition.
3565 input->setText(text.mid(0, 12));
3566 QCOMPARE(input->isInputMethodComposing(), false);
3567 QCOMPARE(spy.count(), 4);
3569 { // Preedit cursor positioned outside (empty) preedit; composing.
3570 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3571 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3572 QGuiApplication::sendEvent(input, &event);
3574 QCOMPARE(input->isInputMethodComposing(), true);
3575 QCOMPARE(spy.count(), 5);
3578 { // Cursor hidden; composing
3579 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3580 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3581 QGuiApplication::sendEvent(input, &event);
3583 QCOMPARE(input->isInputMethodComposing(), true);
3584 QCOMPARE(spy.count(), 5);
3586 { // Default cursor attributes; composing.
3587 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3588 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3589 QGuiApplication::sendEvent(input, &event);
3591 QCOMPARE(input->isInputMethodComposing(), true);
3592 QCOMPARE(spy.count(), 5);
3594 { // Selections are persisted: not composing
3595 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3596 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3597 QGuiApplication::sendEvent(input, &event);
3599 QCOMPARE(input->isInputMethodComposing(), false);
3600 QCOMPARE(spy.count(), 6);
3602 input->setCursorPosition(12);
3604 { // Formatting applied; composing.
3605 QTextCharFormat format;
3606 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3607 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3608 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3609 QGuiApplication::sendEvent(input, &event);
3611 QCOMPARE(input->isInputMethodComposing(), true);
3612 QCOMPARE(spy.count(), 7);
3615 QInputMethodEvent event;
3616 QGuiApplication::sendEvent(input, &event);
3618 QCOMPARE(input->isInputMethodComposing(), false);
3619 QCOMPARE(spy.count(), 8);
3622 void tst_qquicktextinput::inputMethodUpdate()
3624 PlatformInputContext platformInputContext;
3625 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3626 inputMethodPrivate->testContext = &platformInputContext;
3628 QQuickView view(testFileUrl("inputContext.qml"));
3630 view.requestActivateWindow();
3631 QTest::qWaitForWindowShown(&view);
3632 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3633 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3636 // text change even without cursor position change needs to trigger update
3637 input->setText("test");
3638 platformInputContext.clear();
3639 input->setText("xxxx");
3640 QVERIFY(platformInputContext.m_updateCallCount > 0);
3642 // input method event replacing text
3643 platformInputContext.clear();
3645 QInputMethodEvent inputMethodEvent;
3646 inputMethodEvent.setCommitString("y", -1, 1);
3647 QGuiApplication::sendEvent(input, &inputMethodEvent);
3649 QVERIFY(platformInputContext.m_updateCallCount > 0);
3651 // input method changing selection
3652 platformInputContext.clear();
3654 QList<QInputMethodEvent::Attribute> attributes;
3655 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3656 QInputMethodEvent inputMethodEvent("", attributes);
3657 QGuiApplication::sendEvent(input, &inputMethodEvent);
3659 QVERIFY(input->selectionStart() != input->selectionEnd());
3660 QVERIFY(platformInputContext.m_updateCallCount > 0);
3662 // programmatical selections trigger update
3663 platformInputContext.clear();
3665 QVERIFY(platformInputContext.m_updateCallCount > 0);
3668 platformInputContext.clear();
3669 QFont font = input->font();
3670 font.setBold(!font.bold());
3671 input->setFont(font);
3672 QVERIFY(platformInputContext.m_updateCallCount > 0);
3675 platformInputContext.clear();
3677 QInputMethodEvent inputMethodEvent;
3678 inputMethodEvent.setCommitString("y");
3679 QGuiApplication::sendEvent(input, &inputMethodEvent);
3681 QVERIFY(platformInputContext.m_updateCallCount > 0);
3683 // changing cursor position
3684 platformInputContext.clear();
3685 input->setCursorPosition(0);
3686 QVERIFY(platformInputContext.m_updateCallCount > 0);
3688 // read only disabled input method
3689 platformInputContext.clear();
3690 input->setReadOnly(true);
3691 QVERIFY(platformInputContext.m_updateCallCount > 0);
3692 input->setReadOnly(false);
3694 // no updates while no focus
3695 input->setFocus(false);
3696 platformInputContext.clear();
3697 input->setText("Foo");
3698 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3699 input->setCursorPosition(1);
3700 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3702 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3703 input->setReadOnly(true);
3704 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3707 void tst_qquicktextinput::cursorRectangleSize()
3709 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3710 QVERIFY(canvas->rootObject() != 0);
3711 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3713 // make sure cursor rectangle is not at (0,0)
3714 textInput->setX(10);
3715 textInput->setY(10);
3716 textInput->setCursorPosition(3);
3717 QVERIFY(textInput != 0);
3718 textInput->setFocus(true);
3720 canvas->requestActivateWindow();
3721 QTest::qWaitForWindowShown(canvas);
3722 QTRY_VERIFY(qApp->focusObject());
3724 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3725 qApp->sendEvent(qApp->focusObject(), &event);
3726 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3728 QRectF cursorRectFromItem = textInput->cursorRectangle();
3729 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3731 // item and input query cursor rectangles match
3732 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3734 // item cursor rectangle and positionToRectangle calculations match
3735 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3737 // item-canvas transform and input item transform match
3738 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3740 // input panel cursorRectangle property and tranformed item cursor rectangle match
3741 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3742 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3747 void tst_qquicktextinput::tripleClickSelectsAll()
3749 QString qmlfile = testFile("positionAt.qml");
3750 QQuickView view(QUrl::fromLocalFile(qmlfile));
3752 view.requestActivateWindow();
3753 QTest::qWaitForWindowShown(&view);
3755 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3757 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3760 QLatin1String hello("Hello world!");
3761 input->setSelectByMouse(true);
3762 input->setText(hello);
3764 // Clicking on the same point inside TextInput three times in a row
3765 // should trigger a triple click, thus selecting all the text.
3766 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3767 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3768 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3769 QGuiApplication::processEvents();
3770 QCOMPARE(input->selectedText(), hello);
3772 // Now it simulates user moving the mouse between the second and the third click.
3773 // In this situation, we don't expect a triple click.
3774 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3775 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3776 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3777 QGuiApplication::processEvents();
3778 QVERIFY(input->selectedText().isEmpty());
3780 // And now we press the third click too late, so no triple click event is triggered.
3781 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3782 QGuiApplication::processEvents();
3783 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3784 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3785 QGuiApplication::processEvents();
3786 QVERIFY(input->selectedText().isEmpty());
3789 void tst_qquicktextinput::QTBUG_19956_data()
3791 QTest::addColumn<QString>("url");
3792 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3793 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3797 void tst_qquicktextinput::getText_data()
3799 QTest::addColumn<QString>("text");
3800 QTest::addColumn<QString>("inputMask");
3801 QTest::addColumn<int>("start");
3802 QTest::addColumn<int>("end");
3803 QTest::addColumn<QString>("expectedText");
3805 QTest::newRow("all plain text")
3808 << 0 << standard.at(0).length()
3811 QTest::newRow("plain text sub string")
3815 << standard.at(0).mid(0, 12);
3817 QTest::newRow("plain text sub string reversed")
3821 << standard.at(0).mid(0, 12);
3823 QTest::newRow("plain text cropped beginning")
3827 << standard.at(0).mid(0, 4);
3829 QTest::newRow("plain text cropped end")
3832 << 23 << standard.at(0).length() + 8
3833 << standard.at(0).mid(23);
3835 QTest::newRow("plain text cropped beginning and end")
3838 << -9 << standard.at(0).length() + 4
3842 void tst_qquicktextinput::getText()
3844 QFETCH(QString, text);
3845 QFETCH(QString, inputMask);
3848 QFETCH(QString, expectedText);
3850 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3851 QQmlComponent textInputComponent(&engine);
3852 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3853 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3854 QVERIFY(textInput != 0);
3856 QCOMPARE(textInput->getText(start, end), expectedText);
3859 void tst_qquicktextinput::insert_data()
3861 QTest::addColumn<QString>("text");
3862 QTest::addColumn<QString>("inputMask");
3863 QTest::addColumn<int>("selectionStart");
3864 QTest::addColumn<int>("selectionEnd");
3865 QTest::addColumn<int>("insertPosition");
3866 QTest::addColumn<QString>("insertText");
3867 QTest::addColumn<QString>("expectedText");
3868 QTest::addColumn<int>("expectedSelectionStart");
3869 QTest::addColumn<int>("expectedSelectionEnd");
3870 QTest::addColumn<int>("expectedCursorPosition");
3871 QTest::addColumn<bool>("selectionChanged");
3872 QTest::addColumn<bool>("cursorPositionChanged");
3874 QTest::newRow("at cursor position (beginning)")
3879 << QString("Hello") + standard.at(0)
3883 QTest::newRow("at cursor position (end)")
3886 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3888 << standard.at(0) + QString("Hello")
3889 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3892 QTest::newRow("at cursor position (middle)")
3897 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3901 QTest::newRow("after cursor position (beginning)")
3906 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3910 QTest::newRow("before cursor position (end)")
3913 << standard.at(0).length() << standard.at(0).length() << 18
3915 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3916 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3919 QTest::newRow("before cursor position (middle)")
3924 << QString("Hello") + standard.at(0)
3928 QTest::newRow("after cursor position (middle)")
3931 << 18 << 18 << standard.at(0).length()
3933 << standard.at(0) + QString("Hello")
3937 QTest::newRow("before selection")
3942 << QString("Hello") + standard.at(0)
3946 QTest::newRow("before reversed selection")
3951 << QString("Hello") + standard.at(0)
3955 QTest::newRow("after selection")
3958 << 14 << 19 << standard.at(0).length()
3960 << standard.at(0) + QString("Hello")
3964 QTest::newRow("after reversed selection")
3967 << 19 << 14 << standard.at(0).length()
3969 << standard.at(0) + QString("Hello")
3973 QTest::newRow("into selection")
3978 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3982 QTest::newRow("into reversed selection")
3987 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3991 QTest::newRow("rich text into plain text")
3995 << QString("<b>Hello</b>")
3996 << QString("<b>Hello</b>") + standard.at(0)
4000 QTest::newRow("before start")
4009 QTest::newRow("past end")
4012 << 0 << 0 << standard.at(0).length() + 3
4018 const QString inputMask = "009.009.009.009";
4019 const QString ip = "192.168.2.14";
4021 QTest::newRow("mask: at cursor position (beginning)")
4026 << QString("125.168.2.14")
4030 QTest::newRow("mask: at cursor position (end)")
4033 << inputMask.length() << inputMask.length() << inputMask.length()
4036 << inputMask.length() << inputMask.length() << inputMask.length()
4039 QTest::newRow("mask: at cursor position (middle)")
4044 << QString("192.167.5.24")
4048 QTest::newRow("mask: after cursor position (beginning)")
4053 << QString("192.167.5.24")
4057 QTest::newRow("mask: before cursor position (end)")
4060 << inputMask.length() << inputMask.length() << 6
4062 << QString("192.167.5.24")
4063 << inputMask.length() << inputMask.length() << inputMask.length()
4066 QTest::newRow("mask: before cursor position (middle)")
4071 << QString("125.168.2.14")
4075 QTest::newRow("mask: after cursor position (middle)")
4084 QTest::newRow("mask: before selection")
4089 << QString("125.168.2.14")
4093 QTest::newRow("mask: before reversed selection")
4098 << QString("125.168.2.14")
4102 QTest::newRow("mask: after selection")
4111 QTest::newRow("mask: after reversed selection")
4120 QTest::newRow("mask: into selection")
4125 << QString("192.167.5.24")
4129 QTest::newRow("mask: into reversed selection")
4134 << QString("192.167.5.24")
4138 QTest::newRow("mask: before start")
4147 QTest::newRow("mask: past end")
4150 << 0 << 0 << ip.length() + 3
4156 QTest::newRow("mask: invalid characters")
4161 << QString("192.168.2.14")
4165 QTest::newRow("mask: mixed validity")
4169 << QString("a1b2c5")
4170 << QString("125.168.2.14")
4175 void tst_qquicktextinput::insert()
4177 QFETCH(QString, text);
4178 QFETCH(QString, inputMask);
4179 QFETCH(int, selectionStart);
4180 QFETCH(int, selectionEnd);
4181 QFETCH(int, insertPosition);
4182 QFETCH(QString, insertText);
4183 QFETCH(QString, expectedText);
4184 QFETCH(int, expectedSelectionStart);
4185 QFETCH(int, expectedSelectionEnd);
4186 QFETCH(int, expectedCursorPosition);
4187 QFETCH(bool, selectionChanged);
4188 QFETCH(bool, cursorPositionChanged);
4190 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4191 QQmlComponent textInputComponent(&engine);
4192 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4193 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4194 QVERIFY(textInput != 0);
4196 textInput->select(selectionStart, selectionEnd);
4198 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4199 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4200 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4201 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4202 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4204 textInput->insert(insertPosition, insertText);
4206 QCOMPARE(textInput->text(), expectedText);
4207 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4209 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4210 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4211 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4213 if (selectionStart > selectionEnd)
4214 qSwap(selectionStart, selectionEnd);
4216 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4217 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4218 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4219 QCOMPARE(textSpy.count() > 0, text != expectedText);
4220 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4223 void tst_qquicktextinput::remove_data()
4225 QTest::addColumn<QString>("text");
4226 QTest::addColumn<QString>("inputMask");
4227 QTest::addColumn<int>("selectionStart");
4228 QTest::addColumn<int>("selectionEnd");
4229 QTest::addColumn<int>("removeStart");
4230 QTest::addColumn<int>("removeEnd");
4231 QTest::addColumn<QString>("expectedText");
4232 QTest::addColumn<int>("expectedSelectionStart");
4233 QTest::addColumn<int>("expectedSelectionEnd");
4234 QTest::addColumn<int>("expectedCursorPosition");
4235 QTest::addColumn<bool>("selectionChanged");
4236 QTest::addColumn<bool>("cursorPositionChanged");
4238 QTest::newRow("from cursor position (beginning)")
4243 << standard.at(0).mid(5)
4247 QTest::newRow("to cursor position (beginning)")
4252 << standard.at(0).mid(5)
4256 QTest::newRow("to cursor position (end)")
4259 << standard.at(0).length() << standard.at(0).length()
4260 << standard.at(0).length() << standard.at(0).length() - 5
4261 << standard.at(0).mid(0, standard.at(0).length() - 5)
4262 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4265 QTest::newRow("to cursor position (end)")
4268 << standard.at(0).length() << standard.at(0).length()
4269 << standard.at(0).length() - 5 << standard.at(0).length()
4270 << standard.at(0).mid(0, standard.at(0).length() - 5)
4271 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4274 QTest::newRow("from cursor position (middle)")
4279 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4283 QTest::newRow("to cursor position (middle)")
4288 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4292 QTest::newRow("after cursor position (beginning)")
4297 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4301 QTest::newRow("before cursor position (end)")
4304 << standard.at(0).length() << standard.at(0).length()
4306 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4307 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4310 QTest::newRow("before cursor position (middle)")
4315 << standard.at(0).mid(5)
4319 QTest::newRow("after cursor position (middle)")
4324 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4328 QTest::newRow("before selection")
4333 << standard.at(0).mid(5)
4337 QTest::newRow("before reversed selection")
4342 << standard.at(0).mid(5)
4346 QTest::newRow("after selection")
4350 << standard.at(0).length() - 5 << standard.at(0).length()
4351 << standard.at(0).mid(0, standard.at(0).length() - 5)
4355 QTest::newRow("after reversed selection")
4359 << standard.at(0).length() - 5 << standard.at(0).length()
4360 << standard.at(0).mid(0, standard.at(0).length() - 5)
4364 QTest::newRow("from selection")
4369 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4373 QTest::newRow("from reversed selection")
4378 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4382 QTest::newRow("cropped beginning")
4387 << standard.at(0).mid(4)
4391 QTest::newRow("cropped end")
4395 << 23 << standard.at(0).length() + 8
4396 << standard.at(0).mid(0, 23)
4400 QTest::newRow("cropped beginning and end")
4404 << -9 << standard.at(0).length() + 4
4409 const QString inputMask = "009.009.009.009";
4410 const QString ip = "192.168.2.14";
4412 QTest::newRow("mask: from cursor position")
4417 << QString("192.16..14")
4421 QTest::newRow("mask: to cursor position")
4426 << QString("19.8.2.14")
4430 QTest::newRow("mask: before cursor position")
4435 << QString("2.168.2.14")
4439 QTest::newRow("mask: after cursor position")
4444 << QString("192.168.2.")
4448 QTest::newRow("mask: before selection")
4453 << QString("2.168.2.14")
4457 QTest::newRow("mask: before reversed selection")
4462 << QString("2.168.2.14")
4466 QTest::newRow("mask: after selection")
4471 << QString("192.168.2.")
4475 QTest::newRow("mask: after reversed selection")
4480 << QString("192.168.2.")
4484 QTest::newRow("mask: from selection")
4489 << QString("192.168..14")
4493 QTest::newRow("mask: from reversed selection")
4498 << QString("192.168..14")
4502 QTest::newRow("mask: cropped beginning")
4507 << QString(".168.2.14")
4511 QTest::newRow("mask: cropped end")
4516 << QString("192.168.2.1")
4520 QTest::newRow("mask: cropped beginning and end")
4530 void tst_qquicktextinput::remove()
4532 QFETCH(QString, text);
4533 QFETCH(QString, inputMask);
4534 QFETCH(int, selectionStart);
4535 QFETCH(int, selectionEnd);
4536 QFETCH(int, removeStart);
4537 QFETCH(int, removeEnd);
4538 QFETCH(QString, expectedText);
4539 QFETCH(int, expectedSelectionStart);
4540 QFETCH(int, expectedSelectionEnd);
4541 QFETCH(int, expectedCursorPosition);
4542 QFETCH(bool, selectionChanged);
4543 QFETCH(bool, cursorPositionChanged);
4545 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4546 QQmlComponent textInputComponent(&engine);
4547 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4548 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4549 QVERIFY(textInput != 0);
4551 textInput->select(selectionStart, selectionEnd);
4553 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4554 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4555 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4556 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4557 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4559 textInput->remove(removeStart, removeEnd);
4561 QCOMPARE(textInput->text(), expectedText);
4562 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4564 if (selectionStart > selectionEnd) //
4565 qSwap(selectionStart, selectionEnd);
4567 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4568 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4569 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4571 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4572 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4573 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4574 QCOMPARE(textSpy.count() > 0, text != expectedText);
4576 if (cursorPositionChanged) //
4577 QVERIFY(cursorPositionSpy.count() > 0);
4580 void tst_qquicktextinput::keySequence_data()
4582 QTest::addColumn<QString>("text");
4583 QTest::addColumn<QKeySequence>("sequence");
4584 QTest::addColumn<int>("selectionStart");
4585 QTest::addColumn<int>("selectionEnd");
4586 QTest::addColumn<int>("cursorPosition");
4587 QTest::addColumn<QString>("expectedText");
4588 QTest::addColumn<QString>("selectedText");
4589 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4590 QTest::addColumn<Qt::Key>("layoutDirection");
4592 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4594 QTest::newRow("select all")
4595 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4596 << 44 << standard.at(0) << standard.at(0)
4597 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4598 QTest::newRow("select start of line")
4599 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4600 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4601 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4602 QTest::newRow("select start of block")
4603 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4604 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4605 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4606 QTest::newRow("select end of line")
4607 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4608 << 44 << standard.at(0) << standard.at(0).mid(5)
4609 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4610 QTest::newRow("select end of document") // ### Not handled.
4611 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4612 << 3 << standard.at(0) << QString()
4613 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4614 QTest::newRow("select end of block")
4615 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4616 << 44 << standard.at(0) << standard.at(0).mid(18)
4617 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4618 QTest::newRow("delete end of line")
4619 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4620 << 24 << standard.at(0).mid(0, 24) << QString()
4621 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4622 QTest::newRow("move to start of line")
4623 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4624 << 0 << standard.at(0) << QString()
4625 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4626 QTest::newRow("move to start of block")
4627 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4628 << 0 << standard.at(0) << QString()
4629 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4630 QTest::newRow("move to next char")
4631 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4632 << 13 << standard.at(0) << QString()
4633 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4634 QTest::newRow("move to previous char (ltr)")
4635 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4636 << 2 << standard.at(0) << QString()
4637 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4638 QTest::newRow("move to previous char (rtl)")
4639 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4640 << 4 << standard.at(0) << QString()
4641 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4642 QTest::newRow("move to previous char with selection")
4643 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4644 << 3 << standard.at(0) << QString()
4645 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4646 QTest::newRow("select next char (ltr)")
4647 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4648 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4649 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4650 QTest::newRow("select next char (rtl)")
4651 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4652 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4653 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4654 QTest::newRow("select previous char (ltr)")
4655 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4656 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4657 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4658 QTest::newRow("select previous char (rtl)")
4659 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4660 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4661 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4662 QTest::newRow("move to next word (ltr)")
4663 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4664 << 10 << standard.at(0) << QString()
4665 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4666 QTest::newRow("move to next word (rtl)")
4667 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4668 << 4 << standard.at(0) << QString()
4669 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4670 QTest::newRow("move to next word (password,ltr)")
4671 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4672 << 44 << standard.at(0) << QString()
4673 << QQuickTextInput::Password << Qt::Key_Direction_L;
4674 QTest::newRow("move to next word (password,rtl)")
4675 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4676 << 0 << standard.at(0) << QString()
4677 << QQuickTextInput::Password << Qt::Key_Direction_R;
4678 QTest::newRow("move to previous word (ltr)")
4679 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4680 << 4 << standard.at(0) << QString()
4681 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4682 QTest::newRow("move to previous word (rlt)")
4683 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4684 << 10 << standard.at(0) << QString()
4685 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4686 QTest::newRow("move to previous word (password,ltr)")
4687 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4688 << 0 << standard.at(0) << QString()
4689 << QQuickTextInput::Password << Qt::Key_Direction_L;
4690 QTest::newRow("move to previous word (password,rtl)")
4691 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4692 << 44 << standard.at(0) << QString()
4693 << QQuickTextInput::Password << Qt::Key_Direction_R;
4694 QTest::newRow("select next word")
4695 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4696 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4697 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4698 QTest::newRow("select previous word")
4699 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4700 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4701 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4702 QTest::newRow("delete (selection)")
4703 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4704 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4705 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4706 QTest::newRow("delete (no selection)")
4707 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4708 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4709 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4710 QTest::newRow("delete end of word")
4711 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4712 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4713 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4714 QTest::newRow("delete start of word")
4715 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4716 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4717 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4720 void tst_qquicktextinput::keySequence()
4722 QFETCH(QString, text);
4723 QFETCH(QKeySequence, sequence);
4724 QFETCH(int, selectionStart);
4725 QFETCH(int, selectionEnd);
4726 QFETCH(int, cursorPosition);
4727 QFETCH(QString, expectedText);
4728 QFETCH(QString, selectedText);
4729 QFETCH(QQuickTextInput::EchoMode, echoMode);
4730 QFETCH(Qt::Key, layoutDirection);
4732 if (sequence.isEmpty()) {
4733 QSKIP("Key sequence is undefined");
4736 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4737 QQmlComponent textInputComponent(&engine);
4738 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4739 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4740 QVERIFY(textInput != 0);
4741 textInput->setEchoMode(echoMode);
4743 QQuickCanvas canvas;
4744 textInput->setParentItem(canvas.rootItem());
4746 canvas.requestActivateWindow();
4747 QTest::qWaitForWindowShown(&canvas);
4748 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4750 simulateKey(&canvas, layoutDirection);
4752 textInput->select(selectionStart, selectionEnd);
4754 simulateKeys(&canvas, sequence);
4756 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4757 QCOMPARE(textInput->text(), expectedText);
4758 QCOMPARE(textInput->selectedText(), selectedText);
4762 #define REPLACE_UNTIL_END 1
4764 void tst_qquicktextinput::undo_data()
4766 QTest::addColumn<QStringList>("insertString");
4767 QTest::addColumn<IntList>("insertIndex");
4768 QTest::addColumn<IntList>("insertMode");
4769 QTest::addColumn<QStringList>("expectedString");
4770 QTest::addColumn<bool>("use_keys");
4772 for (int i=0; i<2; i++) {
4773 QString keys_str = "keyboard";
4774 bool use_keys = true;
4776 keys_str = "insert";
4781 IntList insertIndex;
4783 QStringList insertString;
4784 QStringList expectedString;
4787 insertMode << NORMAL;
4788 insertString << "1";
4791 insertMode << NORMAL;
4792 insertString << "5";
4795 insertMode << NORMAL;
4796 insertString << "3";
4799 insertMode << NORMAL;
4800 insertString << "2";
4803 insertMode << NORMAL;
4804 insertString << "4";
4806 expectedString << "12345";
4807 expectedString << "1235";
4808 expectedString << "135";
4809 expectedString << "15";
4810 expectedString << "";
4812 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4820 IntList insertIndex;
4822 QStringList insertString;
4823 QStringList expectedString;
4826 insertMode << NORMAL;
4827 insertString << "World"; // World
4830 insertMode << NORMAL;
4831 insertString << "Hello"; // HelloWorld
4834 insertMode << NORMAL;
4835 insertString << "Well"; // WellHelloWorld
4838 insertMode << NORMAL;
4839 insertString << "There"; // WellHelloThereWorld;
4841 expectedString << "WellHelloThereWorld";
4842 expectedString << "WellHelloWorld";
4843 expectedString << "HelloWorld";
4844 expectedString << "World";
4845 expectedString << "";
4847 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4855 IntList insertIndex;
4857 QStringList insertString;
4858 QStringList expectedString;
4861 insertMode << NORMAL;
4862 insertString << "Ensuring";
4865 insertMode << NORMAL;
4866 insertString << " instan";
4869 insertMode << NORMAL;
4870 insertString << "an ";
4873 insertMode << REPLACE_UNTIL_END;
4874 insertString << " unique instance.";
4876 expectedString << "Ensuring a unique instance.";
4877 expectedString << "Ensuring an instan";
4878 expectedString << "Ensuring instan";
4879 expectedString << "";
4881 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4891 void tst_qquicktextinput::undo()
4893 QFETCH(QStringList, insertString);
4894 QFETCH(IntList, insertIndex);
4895 QFETCH(IntList, insertMode);
4896 QFETCH(QStringList, expectedString);
4898 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4899 QQmlComponent textInputComponent(&engine);
4900 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4901 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4902 QVERIFY(textInput != 0);
4904 QQuickCanvas canvas;
4905 textInput->setParentItem(canvas.rootItem());
4907 canvas.requestActivateWindow();
4908 QTest::qWaitForWindowShown(&canvas);
4909 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4911 QVERIFY(!textInput->canUndo());
4913 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4917 // STEP 1: First build up an undo history by inserting or typing some strings...
4918 for (i = 0; i < insertString.size(); ++i) {
4919 if (insertIndex[i] > -1)
4920 textInput->setCursorPosition(insertIndex[i]);
4922 // experimental stuff
4923 if (insertMode[i] == REPLACE_UNTIL_END) {
4924 textInput->select(insertIndex[i], insertIndex[i] + 8);
4926 // This is what I actually want...
4927 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4930 for (int j = 0; j < insertString.at(i).length(); j++)
4931 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4934 QCOMPARE(spy.count(), 1);
4936 // STEP 2: Next call undo several times and see if we can restore to the previous state
4937 for (i = 0; i < expectedString.size() - 1; ++i) {
4938 QCOMPARE(textInput->text(), expectedString[i]);
4939 QVERIFY(textInput->canUndo());
4943 // STEP 3: Verify that we have undone everything
4944 QVERIFY(textInput->text().isEmpty());
4945 QVERIFY(!textInput->canUndo());
4946 QCOMPARE(spy.count(), 2);
4949 void tst_qquicktextinput::redo_data()
4951 QTest::addColumn<QStringList>("insertString");
4952 QTest::addColumn<IntList>("insertIndex");
4953 QTest::addColumn<QStringList>("expectedString");
4956 IntList insertIndex;
4957 QStringList insertString;
4958 QStringList expectedString;
4961 insertString << "World"; // World
4963 insertString << "Hello"; // HelloWorld
4965 insertString << "Well"; // WellHelloWorld
4967 insertString << "There"; // WellHelloThereWorld;
4969 expectedString << "World";
4970 expectedString << "HelloWorld";
4971 expectedString << "WellHelloWorld";
4972 expectedString << "WellHelloThereWorld";
4974 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4978 void tst_qquicktextinput::redo()
4980 QFETCH(QStringList, insertString);
4981 QFETCH(IntList, insertIndex);
4982 QFETCH(QStringList, expectedString);
4984 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4985 QQmlComponent textInputComponent(&engine);
4986 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4987 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4988 QVERIFY(textInput != 0);
4990 QQuickCanvas canvas;
4991 textInput->setParentItem(canvas.rootItem());
4993 canvas.requestActivateWindow();
4994 QTest::qWaitForWindowShown(&canvas);
4995 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4997 QVERIFY(!textInput->canUndo());
4998 QVERIFY(!textInput->canRedo());
5000 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5003 // inserts the diff strings at diff positions
5004 for (i = 0; i < insertString.size(); ++i) {
5005 if (insertIndex[i] > -1)
5006 textInput->setCursorPosition(insertIndex[i]);
5007 for (int j = 0; j < insertString.at(i).length(); j++)
5008 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
5009 QVERIFY(textInput->canUndo());
5010 QVERIFY(!textInput->canRedo());
5013 QCOMPARE(spy.count(), 0);
5016 while (!textInput->text().isEmpty()) {
5017 QVERIFY(textInput->canUndo());
5019 QVERIFY(textInput->canRedo());
5022 QCOMPARE(spy.count(), 1);
5024 for (i = 0; i < expectedString.size(); ++i) {
5025 QVERIFY(textInput->canRedo());
5027 QCOMPARE(textInput->text() , expectedString[i]);
5028 QVERIFY(textInput->canUndo());
5030 QVERIFY(!textInput->canRedo());
5031 QCOMPARE(spy.count(), 2);
5034 void tst_qquicktextinput::undo_keypressevents_data()
5036 QTest::addColumn<KeyList>("keys");
5037 QTest::addColumn<QStringList>("expectedString");
5041 QStringList expectedString;
5044 << QKeySequence::MoveToStartOfLine
5051 << QKeySequence::MoveToEndOfLine
5054 expectedString << "BEVERYAFRAID!";
5055 expectedString << "BEVERYAFRAID";
5056 expectedString << "VERYAFRAID";
5057 expectedString << "AFRAID";
5059 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5062 QStringList expectedString;
5065 keys << "1234" << QKeySequence::MoveToStartOfLine
5067 << Qt::Key_Right << Qt::Key_Right
5069 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5073 expectedString << "12";
5074 expectedString << "1234";
5076 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5079 QStringList expectedString;
5083 << QKeySequence::MoveToStartOfLine
5085 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5087 << QKeySequence::Undo
5089 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5092 expectedString << "AB";
5093 expectedString << "AB12";
5095 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5098 QStringList expectedString;
5103 << Qt::Key_Left << Qt::Key_Left
5107 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5108 // overwriting '1234' with '5'
5110 // undoing deletion of 'AB'
5111 << QKeySequence::Undo
5112 // overwriting '1234' with '6'
5115 expectedString << "ab6cd";
5116 // for versions previous to 3.2 we overwrite needed two undo operations
5117 expectedString << "ab1234cd";
5118 expectedString << "abcd";
5120 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5123 QStringList expectedString;
5128 << Qt::Key_Backspace;
5130 expectedString << "AB";
5131 expectedString << "ABC";
5133 QTest::newRow("Inserts,backspace") << keys << expectedString;
5136 QStringList expectedString;
5140 << Qt::Key_Backspace
5144 expectedString << "ABZ";
5145 expectedString << "AB";
5146 expectedString << "ABC";
5148 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5151 QStringList expectedString;
5154 keys << "123" << QKeySequence::MoveToStartOfLine
5156 << QKeySequence::SelectEndOfLine
5157 // overwriting '123' with 'ABC'
5160 expectedString << "ABC";
5161 // for versions previous to 3.2 we overwrite needed two undo operations
5162 expectedString << "123";
5164 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5167 QStringList expectedString;
5171 << QKeySequence::Undo
5172 << QKeySequence::Redo;
5174 expectedString << "123";
5175 expectedString << QString();
5177 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5181 void tst_qquicktextinput::undo_keypressevents()
5183 QFETCH(KeyList, keys);
5184 QFETCH(QStringList, expectedString);
5186 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5187 QQmlComponent textInputComponent(&engine);
5188 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5189 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5190 QVERIFY(textInput != 0);
5192 QQuickCanvas canvas;
5193 textInput->setParentItem(canvas.rootItem());
5195 canvas.requestActivateWindow();
5196 QTest::qWaitForWindowShown(&canvas);
5197 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5199 simulateKeys(&canvas, keys);
5201 for (int i = 0; i < expectedString.size(); ++i) {
5202 QCOMPARE(textInput->text() , expectedString[i]);
5205 QVERIFY(textInput->text().isEmpty());
5208 void tst_qquicktextinput::QTBUG_19956()
5210 QFETCH(QString, url);
5212 QQuickView canvas(testFileUrl(url));
5214 canvas.requestActivateWindow();
5215 QTest::qWaitForWindowShown(&canvas);
5216 QVERIFY(canvas.rootObject() != 0);
5217 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5219 input->setFocus(true);
5220 QVERIFY(input->hasActiveFocus());
5222 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
5223 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5224 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
5225 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5227 canvas.rootObject()->setProperty("topvalue", 15);
5228 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
5229 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5231 canvas.rootObject()->setProperty("topvalue", 25);
5232 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
5233 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5235 canvas.rootObject()->setProperty("bottomvalue", 21);
5236 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
5237 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5239 canvas.rootObject()->setProperty("bottomvalue", 10);
5240 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5241 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5244 void tst_qquicktextinput::QTBUG_19956_regexp()
5246 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5248 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5249 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5251 QQuickView canvas(url);
5253 canvas.requestActivateWindow();
5254 QTest::qWaitForWindowShown(&canvas);
5255 QVERIFY(canvas.rootObject() != 0);
5256 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5258 input->setFocus(true);
5259 QVERIFY(input->hasActiveFocus());
5261 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5262 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5263 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
5264 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5266 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5267 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5268 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5270 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5271 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5272 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5275 void tst_qquicktextinput::implicitSize_data()
5277 QTest::addColumn<QString>("text");
5278 QTest::addColumn<QString>("wrap");
5279 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5280 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5283 void tst_qquicktextinput::implicitSize()
5285 QFETCH(QString, text);
5286 QFETCH(QString, wrap);
5287 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5288 QQmlComponent textComponent(&engine);
5289 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5290 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5292 QVERIFY(textObject->width() < textObject->implicitWidth());
5293 QVERIFY(textObject->height() == textObject->implicitHeight());
5295 textObject->resetWidth();
5296 QVERIFY(textObject->width() == textObject->implicitWidth());
5297 QVERIFY(textObject->height() == textObject->implicitHeight());
5300 void tst_qquicktextinput::implicitSizeBinding_data()
5302 implicitSize_data();
5305 void tst_qquicktextinput::implicitSizeBinding()
5307 QFETCH(QString, text);
5308 QFETCH(QString, wrap);
5309 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5310 QQmlComponent textComponent(&engine);
5311 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5312 QScopedPointer<QObject> object(textComponent.create());
5313 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5315 QCOMPARE(textObject->width(), textObject->implicitWidth());
5316 QCOMPARE(textObject->height(), textObject->implicitHeight());
5318 textObject->resetWidth();
5319 QCOMPARE(textObject->width(), textObject->implicitWidth());
5320 QCOMPARE(textObject->height(), textObject->implicitHeight());
5322 textObject->resetHeight();
5323 QCOMPARE(textObject->width(), textObject->implicitWidth());
5324 QCOMPARE(textObject->height(), textObject->implicitHeight());
5328 void tst_qquicktextinput::negativeDimensions()
5330 // Verify this doesn't assert during initialization.
5331 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5332 QScopedPointer<QObject> o(component.create());
5334 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5336 QCOMPARE(input->width(), qreal(-1));
5337 QCOMPARE(input->height(), qreal(-1));
5341 void tst_qquicktextinput::setInputMask_data()
5343 QTest::addColumn<QString>("mask");
5344 QTest::addColumn<QString>("input");
5345 QTest::addColumn<QString>("expectedText");
5346 QTest::addColumn<QString>("expectedDisplay");
5347 QTest::addColumn<bool>("insert_text");
5349 // both keyboard and insert()
5350 for (int i=0; i<2; i++) {
5351 bool insert_text = i==0 ? false : true;
5352 QString insert_mode = "keys ";
5354 insert_mode = "insert ";
5356 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5357 << QString("000.000.000.000")
5358 << QString("127.0.0.1")
5359 << QString("127.0.0.1")
5360 << QString("127.0 .0 .1 ")
5361 << bool(insert_text);
5362 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5363 << QString("HH:HH:HH:HH:HH:HH;#")
5364 << QString("00:E0:81:21:9E:8E")
5365 << QString("00:E0:81:21:9E:8E")
5366 << QString("00:E0:81:21:9E:8E")
5367 << bool(insert_text);
5368 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5369 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5370 << QString("AAe081219E8E")
5371 << QString("aa:E0:81:21:9E:8E")
5372 << QString("aa:E0:81:21:9E:8E")
5373 << bool(insert_text);
5374 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5375 << QString("BBBBBBBB;0")
5376 << QString("11011001")
5378 << QString("11011001")
5379 << bool(insert_text);
5380 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5381 << QString("bbbb.bbbb;-")
5382 << QString("110. 0001")
5383 << QString("110.0001")
5384 << QString("110-.0001")
5385 << bool(insert_text);
5386 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5387 << QString("000.000.000.000;0")
5388 << QString("127.0.0.1")
5389 << QString("127...1")
5390 << QString("127.000.000.100")
5391 << bool(insert_text);
5392 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5393 << QString("000.000.000.000")
5394 << QString(".0.0.1")
5395 << QString(".0.0.1")
5396 << QString(" .0 .0 .1 ")
5397 << bool(insert_text);
5398 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5399 << QString("000.000.000.000")
5402 << QString(" . . . ")
5403 << bool(insert_text);
5404 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5405 << QString("000.000.000.000;#")
5408 << QString("###.###.###.###")
5409 << bool(insert_text);
5410 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5411 << QString("000.000.000.000")
5412 << QString("1234123412341234")
5413 << QString("123.412.341.234")
5414 << QString("123.412.341.234")
5415 << bool(insert_text);
5416 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5421 << bool(insert_text);
5422 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5427 << bool(insert_text);
5429 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5434 << bool(insert_text);
5435 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5436 << QString("!A!A!A!A")
5440 << bool(insert_text);
5441 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5446 << bool(insert_text);
5448 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5449 << QString("{n}[0]")
5453 << bool(insert_text);
5454 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5455 << QString("\\\\N\\\\n00")
5459 << bool(insert_text);
5460 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5461 << QString("\\\\\\\\00")
5465 << bool(insert_text);
5466 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5467 << QString("\\\\(00\\\\)")
5471 << bool(insert_text);
5473 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5474 << QString(">AAAA<AAAA!AAAA")
5475 << QString("AbCdEfGhIjKl")
5476 << QString("ABCDefghIjKl")
5477 << QString("ABCDefghIjKl")
5478 << bool(insert_text);
5479 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5480 << QString(">aaaa<aaaa!aaaa")
5481 << QString("AbCdEfGhIjKl")
5482 << QString("ABCDefghIjKl")
5483 << QString("ABCDefghIjKl")
5484 << bool(insert_text);
5486 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5487 << QString(">A<A<A>A>A<A!A!A")
5488 << QString("AbCdEFGH")
5489 << QString("AbcDEfGH")
5490 << QString("AbcDEfGH")
5491 << bool(insert_text);
5492 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5493 << QString(">A<A<A>A>A<A!A!A")
5494 << QString("aBcDefgh")
5495 << QString("AbcDEfgh")
5496 << QString("AbcDEfgh")
5497 << bool(insert_text);
5498 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5499 << QString(">a<a<a>a>a<a!a!a")
5500 << QString("AbCdEFGH")
5501 << QString("AbcDEfGH")
5502 << QString("AbcDEfGH")
5503 << bool(insert_text);
5504 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5505 << QString(">a<a<a>a>a<a!a!a")
5506 << QString("aBcDefgh")
5507 << QString("AbcDEfgh")
5508 << QString("AbcDEfgh")
5509 << bool(insert_text);
5510 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5511 << QString(">H<H<H>H>H<H!H!H")
5512 << QString("aBcDef01")
5513 << QString("AbcDEf01")
5514 << QString("AbcDEf01")
5515 << bool(insert_text);
5516 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5517 << QString(">h<h<h>h>h<h!h!h")
5518 << QString("aBcDef92")
5519 << QString("AbcDEf92")
5520 << QString("AbcDEf92")
5521 << bool(insert_text);
5523 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5524 << QString("AAAAAAAA")
5525 << QString("A2#a;.0!")
5528 << bool(insert_text);
5529 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5531 << QString("f4f4f4f4")
5534 << bool(insert_text);
5535 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5536 << QString("9999;0")
5540 << bool(insert_text);
5544 void tst_qquicktextinput::setInputMask()
5546 QFETCH(QString, mask);
5547 QFETCH(QString, input);
5548 QFETCH(QString, expectedText);
5549 QFETCH(QString, expectedDisplay);
5550 QFETCH(bool, insert_text);
5552 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5553 QQmlComponent textInputComponent(&engine);
5554 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5555 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5556 QVERIFY(textInput != 0);
5558 // then either insert using insert() or keyboard
5560 textInput->insert(0, input);
5562 QQuickCanvas canvas;
5563 textInput->setParentItem(canvas.rootItem());
5565 canvas.requestActivateWindow();
5566 QTest::qWaitForWindowShown(&canvas);
5567 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5569 simulateKey(&canvas, Qt::Key_Home);
5570 for (int i = 0; i < input.length(); i++)
5571 QTest::keyClick(&canvas, input.at(i).toLatin1());
5574 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5575 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5577 QCOMPARE(textInput->text(), expectedText);
5578 QCOMPARE(textInput->displayText(), expectedDisplay);
5581 void tst_qquicktextinput::inputMask_data()
5583 QTest::addColumn<QString>("mask");
5584 QTest::addColumn<QString>("expectedMask");
5586 // if no mask is set a nul string should be returned
5587 QTest::newRow("nul 1") << QString("") << QString();
5588 QTest::newRow("nul 2") << QString() << QString();
5590 // try different masks
5591 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5592 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5593 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5594 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5596 // set an invalid input mask...
5597 // the current behaviour is that this exact (faulty) string is returned.
5598 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5600 // verify that we can unset the mask again
5601 QTest::newRow("unset") << QString("") << QString();
5604 void tst_qquicktextinput::inputMask()
5606 QFETCH(QString, mask);
5607 QFETCH(QString, expectedMask);
5609 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5610 QQmlComponent textInputComponent(&engine);
5611 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5612 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5613 QVERIFY(textInput != 0);
5615 QCOMPARE(textInput->inputMask(), expectedMask);
5618 void tst_qquicktextinput::clearInputMask()
5620 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
5621 QQmlComponent textInputComponent(&engine);
5622 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5623 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5624 QVERIFY(textInput != 0);
5626 QVERIFY(textInput->inputMask() != QString());
5627 textInput->setInputMask(QString());
5628 QCOMPARE(textInput->inputMask(), QString());
5631 void tst_qquicktextinput::keypress_inputMask_data()
5633 QTest::addColumn<QString>("mask");
5634 QTest::addColumn<KeyList>("keys");
5635 QTest::addColumn<QString>("expectedText");
5636 QTest::addColumn<QString>("expectedDisplayText");
5640 // inserting 'A1.2B'
5641 keys << Qt::Key_Home << "A1.2B";
5642 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
5647 keys << Qt::Key_Home << "0!P3";
5648 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
5653 keys << Qt::Key_Home
5655 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5659 // selecting all and delete
5660 keys << Qt::Key_Home
5661 << Key(Qt::ShiftModifier, Qt::Key_End)
5663 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5667 // inserting '12.12' then two backspaces
5668 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
5669 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
5674 keys << Qt::Key_Home << "12ab";
5675 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
5679 void tst_qquicktextinput::keypress_inputMask()
5681 QFETCH(QString, mask);
5682 QFETCH(KeyList, keys);
5683 QFETCH(QString, expectedText);
5684 QFETCH(QString, expectedDisplayText);
5686 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5687 QQmlComponent textInputComponent(&engine);
5688 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5689 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5690 QVERIFY(textInput != 0);
5692 QQuickCanvas canvas;
5693 textInput->setParentItem(canvas.rootItem());
5695 canvas.requestActivateWindow();
5696 QTest::qWaitForWindowShown(&canvas);
5697 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5699 simulateKeys(&canvas, keys);
5701 QCOMPARE(textInput->text(), expectedText);
5702 QCOMPARE(textInput->displayText(), expectedDisplayText);
5706 void tst_qquicktextinput::hasAcceptableInputMask_data()
5708 QTest::addColumn<QString>("optionalMask");
5709 QTest::addColumn<QString>("requiredMask");
5710 QTest::addColumn<QString>("invalid");
5711 QTest::addColumn<QString>("valid");
5713 QTest::newRow("Alphabetic optional and required")
5714 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
5715 QTest::newRow("Alphanumeric optional and require")
5716 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
5717 QTest::newRow("Any optional and required")
5718 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
5719 QTest::newRow("Numeric (0-9) required")
5720 << QString("0000") << QString("9999") << QString("11") << QString("1138");
5721 QTest::newRow("Numeric (1-9) optional and required")
5722 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
5725 void tst_qquicktextinput::hasAcceptableInputMask()
5727 QFETCH(QString, optionalMask);
5728 QFETCH(QString, requiredMask);
5729 QFETCH(QString, invalid);
5730 QFETCH(QString, valid);
5732 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + optionalMask + "\" }";
5733 QQmlComponent textInputComponent(&engine);
5734 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5735 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5736 QVERIFY(textInput != 0);
5738 QQuickCanvas canvas;
5739 textInput->setParentItem(canvas.rootItem());
5741 canvas.requestActivateWindow();
5742 QTest::qWaitForWindowShown(&canvas);
5743 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5745 // test that invalid input (for required) work for optionalMask
5746 textInput->setText(invalid);
5747 QVERIFY(textInput->hasAcceptableInput());
5749 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
5750 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
5752 // test requiredMask
5753 textInput->setInputMask(requiredMask);
5754 textInput->setText(invalid);
5755 QVERIFY(!textInput->hasAcceptableInput());
5757 textInput->setText(valid);
5758 QVERIFY(textInput->hasAcceptableInput());
5761 void tst_qquicktextinput::maskCharacter_data()
5763 QTest::addColumn<QString>("mask");
5764 QTest::addColumn<QString>("input");
5765 QTest::addColumn<bool>("expectedValid");
5767 QTest::newRow("Hex") << QString("H")
5768 << QString("0123456789abcdefABCDEF") << true;
5769 QTest::newRow("hex") << QString("h")
5770 << QString("0123456789abcdefABCDEF") << true;
5771 QTest::newRow("HexInvalid") << QString("H")
5772 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5774 QTest::newRow("hexInvalid") << QString("h")
5775 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5777 QTest::newRow("Bin") << QString("B")
5778 << QString("01") << true;
5779 QTest::newRow("bin") << QString("b")
5780 << QString("01") << true;
5781 QTest::newRow("BinInvalid") << QString("B")
5782 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5784 QTest::newRow("binInvalid") << QString("b")
5785 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5789 void tst_qquicktextinput::maskCharacter()
5791 QFETCH(QString, mask);
5792 QFETCH(QString, input);
5793 QFETCH(bool, expectedValid);
5795 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5796 QQmlComponent textInputComponent(&engine);
5797 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5798 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5799 QVERIFY(textInput != 0);
5801 QQuickCanvas canvas;
5802 textInput->setParentItem(canvas.rootItem());
5804 canvas.requestActivateWindow();
5805 QTest::qWaitForWindowShown(&canvas);
5806 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5808 for (int i = 0; i < input.size(); ++i) {
5809 QString in = QString(input.at(i));
5810 QString expected = expectedValid ? in : QString();
5811 textInput->setText(QString(input.at(i)));
5812 QCOMPARE(textInput->text(), expected);
5816 QTEST_MAIN(tst_qquicktextinput)
5818 #include "tst_qquicktextinput.moc"