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();
141 void passwordCharacter();
142 void cursorDelegate_data();
143 void cursorDelegate();
144 void cursorVisible();
145 void cursorRectangle_data();
146 void cursorRectangle();
148 void navigation_RTL();
150 void copyAndPasteKeySequence();
151 void canPasteEmpty();
156 void openInputPanel();
157 void setHAlignClearCache();
158 void focusOutClearSelection();
161 void passwordEchoDelay();
162 void geometrySignals();
165 void preeditAutoScroll();
166 void preeditCursorRectangle();
167 void inputContextMouseHandler();
168 void inputMethodComposing();
169 void inputMethodUpdate();
170 void cursorRectangleSize();
179 void keySequence_data();
186 void undo_keypressevents_data();
187 void undo_keypressevents();
190 void QTBUG_19956_data();
191 void QTBUG_19956_regexp();
193 void implicitSize_data();
195 void implicitSizeBinding_data();
196 void implicitSizeBinding();
198 void negativeDimensions();
201 void setInputMask_data();
203 void inputMask_data();
205 void clearInputMask();
206 void keypress_inputMask_data();
207 void keypress_inputMask();
208 void hasAcceptableInputMask_data();
209 void hasAcceptableInputMask();
210 void maskCharacter_data();
211 void maskCharacter();
214 void simulateKey(QWindow *, int key);
216 void simulateKeys(QWindow *window, const QList<Key> &keys);
217 void simulateKeys(QWindow *window, const QKeySequence &sequence);
220 QStringList standard;
221 QStringList colorStrings;
224 typedef QList<int> IntList;
225 Q_DECLARE_METATYPE(IntList)
227 typedef QList<Key> KeyList;
228 Q_DECLARE_METATYPE(KeyList)
230 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
232 for (int i = 0; i < keys.count(); ++i) {
233 const int key = keys.at(i).first;
234 const int modifiers = key & Qt::KeyboardModifierMask;
235 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
237 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
238 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
240 QGuiApplication::sendEvent(window, &press);
241 QGuiApplication::sendEvent(window, &release);
245 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
247 for (int i = 0; i < sequence.count(); ++i) {
248 const int key = sequence[i];
249 const int modifiers = key & Qt::KeyboardModifierMask;
251 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
255 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
257 for (int i = 0; i < sequence.count(); ++i)
258 keys << Key(sequence[i], QChar());
262 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
264 for (int i = 0; i < N - 1; ++i) {
265 int key = QTest::asciiToKey(characters[i]);
266 QChar character = QLatin1Char(characters[i]);
267 keys << Key(key, character);
272 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
274 keys << Key(key, QChar());
278 void tst_qquicktextinput::cleanup()
280 // ensure not even skipped tests with custom input context leave it dangling
281 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
282 inputMethodPrivate->testContext = 0;
285 tst_qquicktextinput::tst_qquicktextinput()
287 standard << "the quick brown fox jumped over the lazy dog"
288 << "It's supercalifragisiticexpialidocious!"
293 colorStrings << "aliceblue"
307 void tst_qquicktextinput::text()
310 QQmlComponent textinputComponent(&engine);
311 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
312 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
314 QVERIFY(textinputObject != 0);
315 QCOMPARE(textinputObject->text(), QString(""));
316 QCOMPARE(textinputObject->length(), 0);
318 delete textinputObject;
321 for (int i = 0; i < standard.size(); i++)
323 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
324 QQmlComponent textinputComponent(&engine);
325 textinputComponent.setData(componentStr.toLatin1(), QUrl());
326 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
328 QVERIFY(textinputObject != 0);
329 QCOMPARE(textinputObject->text(), standard.at(i));
330 QCOMPARE(textinputObject->length(), standard.at(i).length());
332 delete textinputObject;
337 void tst_qquicktextinput::width()
339 // uses Font metrics to find the width for standard
341 QQmlComponent textinputComponent(&engine);
342 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
343 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
345 QVERIFY(textinputObject != 0);
346 QCOMPARE(textinputObject->width(), 0.0);
348 delete textinputObject;
351 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
353 for (int i = 0; i < standard.size(); i++)
355 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
356 QQmlComponent textinputComponent(&engine);
357 textinputComponent.setData(componentStr.toLatin1(), QUrl());
358 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
360 QString s = standard.at(i);
361 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
363 QTextLayout layout(s);
364 layout.setFont(textinputObject->font());
365 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
366 if (requiresUnhintedMetrics) {
368 option.setUseDesignMetrics(true);
369 layout.setTextOption(option);
372 layout.beginLayout();
374 QTextLine line = layout.createLine();
381 qreal metricWidth = ceil(layout.boundingRect().width());
383 QVERIFY(textinputObject != 0);
384 int delta = abs(int(int(textinputObject->width()) - metricWidth));
385 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
387 delete textinputObject;
391 void tst_qquicktextinput::font()
393 //test size, then bold, then italic, then family
395 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
396 QQmlComponent textinputComponent(&engine);
397 textinputComponent.setData(componentStr.toLatin1(), QUrl());
398 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
400 QVERIFY(textinputObject != 0);
401 QCOMPARE(textinputObject->font().pointSize(), 40);
402 QCOMPARE(textinputObject->font().bold(), false);
403 QCOMPARE(textinputObject->font().italic(), false);
405 delete textinputObject;
409 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
410 QQmlComponent textinputComponent(&engine);
411 textinputComponent.setData(componentStr.toLatin1(), QUrl());
412 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
414 QVERIFY(textinputObject != 0);
415 QCOMPARE(textinputObject->font().bold(), true);
416 QCOMPARE(textinputObject->font().italic(), false);
418 delete textinputObject;
422 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
423 QQmlComponent textinputComponent(&engine);
424 textinputComponent.setData(componentStr.toLatin1(), QUrl());
425 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
427 QVERIFY(textinputObject != 0);
428 QCOMPARE(textinputObject->font().italic(), true);
429 QCOMPARE(textinputObject->font().bold(), false);
431 delete textinputObject;
435 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
436 QQmlComponent textinputComponent(&engine);
437 textinputComponent.setData(componentStr.toLatin1(), QUrl());
438 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
440 QVERIFY(textinputObject != 0);
441 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
442 QCOMPARE(textinputObject->font().bold(), false);
443 QCOMPARE(textinputObject->font().italic(), false);
445 delete textinputObject;
449 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
450 QQmlComponent textinputComponent(&engine);
451 textinputComponent.setData(componentStr.toLatin1(), QUrl());
452 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
454 QVERIFY(textinputObject != 0);
455 QCOMPARE(textinputObject->font().family(), QString(""));
457 delete textinputObject;
461 void tst_qquicktextinput::color()
464 for (int i = 0; i < colorStrings.size(); i++)
466 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
467 QQmlComponent textinputComponent(&engine);
468 textinputComponent.setData(componentStr.toLatin1(), QUrl());
469 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
470 QVERIFY(textinputObject != 0);
471 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
473 delete textinputObject;
476 //test selection color
477 for (int i = 0; i < colorStrings.size(); i++)
479 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
480 QQmlComponent textinputComponent(&engine);
481 textinputComponent.setData(componentStr.toLatin1(), QUrl());
482 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
483 QVERIFY(textinputObject != 0);
484 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
486 delete textinputObject;
489 //test selected text color
490 for (int i = 0; i < colorStrings.size(); i++)
492 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
493 QQmlComponent textinputComponent(&engine);
494 textinputComponent.setData(componentStr.toLatin1(), QUrl());
495 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
496 QVERIFY(textinputObject != 0);
497 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
499 delete textinputObject;
503 QString colorStr = "#AA001234";
504 QColor testColor("#001234");
505 testColor.setAlpha(170);
507 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
508 QQmlComponent textinputComponent(&engine);
509 textinputComponent.setData(componentStr.toLatin1(), QUrl());
510 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
512 QVERIFY(textinputObject != 0);
513 QCOMPARE(textinputObject->color(), testColor);
515 delete textinputObject;
519 void tst_qquicktextinput::wrap()
522 // for specified width and wrap set true
524 QQmlComponent textComponent(&engine);
525 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
526 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
527 textHeight = textObject->height();
529 QVERIFY(textObject != 0);
530 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
531 QCOMPARE(textObject->width(), 300.);
536 for (int i = 0; i < standard.count(); i++) {
537 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
538 QQmlComponent textComponent(&engine);
539 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
540 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
542 QVERIFY(textObject != 0);
543 QCOMPARE(textObject->width(), 30.);
544 QVERIFY(textObject->height() > textHeight);
546 int oldHeight = textObject->height();
547 textObject->setWidth(100);
548 QVERIFY(textObject->height() < oldHeight);
554 void tst_qquicktextinput::selection()
556 QString testStr = standard[0];
557 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
558 QQmlComponent textinputComponent(&engine);
559 textinputComponent.setData(componentStr.toLatin1(), QUrl());
560 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
561 QVERIFY(textinputObject != 0);
564 //Test selection follows cursor
565 for (int i=0; i<= testStr.size(); i++) {
566 textinputObject->setCursorPosition(i);
567 QCOMPARE(textinputObject->cursorPosition(), i);
568 QCOMPARE(textinputObject->selectionStart(), i);
569 QCOMPARE(textinputObject->selectionEnd(), i);
570 QVERIFY(textinputObject->selectedText().isNull());
573 textinputObject->setCursorPosition(0);
574 QVERIFY(textinputObject->cursorPosition() == 0);
575 QVERIFY(textinputObject->selectionStart() == 0);
576 QVERIFY(textinputObject->selectionEnd() == 0);
577 QVERIFY(textinputObject->selectedText().isNull());
579 // Verify invalid positions are ignored.
580 textinputObject->setCursorPosition(-1);
581 QVERIFY(textinputObject->cursorPosition() == 0);
582 QVERIFY(textinputObject->selectionStart() == 0);
583 QVERIFY(textinputObject->selectionEnd() == 0);
584 QVERIFY(textinputObject->selectedText().isNull());
586 textinputObject->setCursorPosition(textinputObject->text().count()+1);
587 QVERIFY(textinputObject->cursorPosition() == 0);
588 QVERIFY(textinputObject->selectionStart() == 0);
589 QVERIFY(textinputObject->selectionEnd() == 0);
590 QVERIFY(textinputObject->selectedText().isNull());
593 for (int i=0; i<= testStr.size(); i++) {
594 textinputObject->select(0,i);
595 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
597 for (int i=0; i<= testStr.size(); i++) {
598 textinputObject->select(i,testStr.size());
599 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
602 textinputObject->setCursorPosition(0);
603 QVERIFY(textinputObject->cursorPosition() == 0);
604 QVERIFY(textinputObject->selectionStart() == 0);
605 QVERIFY(textinputObject->selectionEnd() == 0);
606 QVERIFY(textinputObject->selectedText().isNull());
608 //Test Error Ignoring behaviour
609 textinputObject->setCursorPosition(0);
610 QVERIFY(textinputObject->selectedText().isNull());
611 textinputObject->select(-10,0);
612 QVERIFY(textinputObject->selectedText().isNull());
613 textinputObject->select(100,110);
614 QVERIFY(textinputObject->selectedText().isNull());
615 textinputObject->select(0,-10);
616 QVERIFY(textinputObject->selectedText().isNull());
617 textinputObject->select(0,100);
618 QVERIFY(textinputObject->selectedText().isNull());
619 textinputObject->select(0,10);
620 QVERIFY(textinputObject->selectedText().size() == 10);
621 textinputObject->select(-10,10);
622 QVERIFY(textinputObject->selectedText().size() == 10);
623 textinputObject->select(100,101);
624 QVERIFY(textinputObject->selectedText().size() == 10);
625 textinputObject->select(0,-10);
626 QVERIFY(textinputObject->selectedText().size() == 10);
627 textinputObject->select(0,100);
628 QVERIFY(textinputObject->selectedText().size() == 10);
630 textinputObject->deselect();
631 QVERIFY(textinputObject->selectedText().isNull());
632 textinputObject->select(0,10);
633 QVERIFY(textinputObject->selectedText().size() == 10);
634 textinputObject->deselect();
635 QVERIFY(textinputObject->selectedText().isNull());
637 // test input method selection
638 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
639 textinputObject->setFocus(true);
641 QList<QInputMethodEvent::Attribute> attributes;
642 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
643 QInputMethodEvent event("", attributes);
644 QGuiApplication::sendEvent(textinputObject, &event);
646 QCOMPARE(selectionSpy.count(), 1);
647 QCOMPARE(textinputObject->selectionStart(), 12);
648 QCOMPARE(textinputObject->selectionEnd(), 17);
650 delete textinputObject;
653 void tst_qquicktextinput::persistentSelection()
655 QQuickView canvas(testFileUrl("persistentSelection.qml"));
657 canvas.requestActivateWindow();
658 QTest::qWaitForWindowShown(&canvas);
659 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
660 canvas.requestActivateWindow();
662 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
664 QVERIFY(input->hasActiveFocus());
666 QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
668 QCOMPARE(input->persistentSelection(), false);
670 input->setPersistentSelection(false);
671 QCOMPARE(input->persistentSelection(), false);
672 QCOMPARE(spy.count(), 0);
675 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
677 input->setFocus(false);
678 QCOMPARE(input->property("selected").toString(), QString());
680 input->setFocus(true);
681 QCOMPARE(input->property("selected").toString(), QString());
683 input->setPersistentSelection(true);
684 QCOMPARE(input->persistentSelection(), true);
685 QCOMPARE(spy.count(), 1);
688 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
690 input->setFocus(false);
691 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
693 input->setFocus(true);
694 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
697 void tst_qquicktextinput::isRightToLeft_data()
699 QTest::addColumn<QString>("text");
700 QTest::addColumn<bool>("emptyString");
701 QTest::addColumn<bool>("firstCharacter");
702 QTest::addColumn<bool>("lastCharacter");
703 QTest::addColumn<bool>("middleCharacter");
704 QTest::addColumn<bool>("startString");
705 QTest::addColumn<bool>("midString");
706 QTest::addColumn<bool>("endString");
708 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
709 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
710 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
711 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
712 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
713 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;
714 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
717 void tst_qquicktextinput::isRightToLeft()
719 QFETCH(QString, text);
720 QFETCH(bool, emptyString);
721 QFETCH(bool, firstCharacter);
722 QFETCH(bool, lastCharacter);
723 QFETCH(bool, middleCharacter);
724 QFETCH(bool, startString);
725 QFETCH(bool, midString);
726 QFETCH(bool, endString);
728 QQuickTextInput textInput;
729 textInput.setText(text);
731 // first test that the right string is delivered to the QString::isRightToLeft()
732 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
733 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
734 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
735 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
736 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
737 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
739 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
740 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
742 // then test that the feature actually works
743 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
744 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
745 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
746 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
747 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
748 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
750 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
751 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
754 void tst_qquicktextinput::moveCursorSelection_data()
756 QTest::addColumn<QString>("testStr");
757 QTest::addColumn<int>("cursorPosition");
758 QTest::addColumn<int>("movePosition");
759 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
760 QTest::addColumn<int>("selectionStart");
761 QTest::addColumn<int>("selectionEnd");
762 QTest::addColumn<bool>("reversible");
764 // () contains the text selected by the cursor.
765 // <> contains the actual selection.
767 QTest::newRow("(t)he|characters")
768 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
769 QTest::newRow("do(g)|characters")
770 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
771 QTest::newRow("jum(p)ed|characters")
772 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
773 QTest::newRow("jumped( )over|characters")
774 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
775 QTest::newRow("(the )|characters")
776 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
777 QTest::newRow("( dog)|characters")
778 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
779 QTest::newRow("( jumped )|characters")
780 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
781 QTest::newRow("th(e qu)ick|characters")
782 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
783 QTest::newRow("la(zy d)og|characters")
784 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
785 QTest::newRow("jum(ped ov)er|characters")
786 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
787 QTest::newRow("()the|characters")
788 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
789 QTest::newRow("dog()|characters")
790 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
791 QTest::newRow("jum()ped|characters")
792 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
794 QTest::newRow("<(t)he>|words")
795 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
796 QTest::newRow("<do(g)>|words")
797 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
798 QTest::newRow("<jum(p)ed>|words")
799 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
800 QTest::newRow("<jumped( )>over|words,ltr")
801 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
802 QTest::newRow("jumped<( )over>|words,rtl")
803 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
804 QTest::newRow("<(the )>quick|words,ltr")
805 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
806 QTest::newRow("<(the )quick>|words,rtl")
807 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
808 QTest::newRow("<lazy( dog)>|words,ltr")
809 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
810 QTest::newRow("lazy<( dog)>|words,rtl")
811 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
812 QTest::newRow("<fox( jumped )>over|words,ltr")
813 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
814 QTest::newRow("fox<( jumped )over>|words,rtl")
815 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
816 QTest::newRow("<th(e qu)ick>|words")
817 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
818 QTest::newRow("<la(zy d)og|words>")
819 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
820 QTest::newRow("<jum(ped ov)er>|words")
821 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
822 QTest::newRow("<()>the|words")
823 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
824 QTest::newRow("dog<()>|words")
825 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
826 QTest::newRow("jum<()>ped|words")
827 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
829 QTest::newRow("Hello<(,)> |words")
830 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
831 QTest::newRow("Hello<(, )>world|words,ltr")
832 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
833 QTest::newRow("Hello<(, )world>|words,rtl")
834 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
835 QTest::newRow("<Hel(lo, )>world|words,ltr")
836 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
837 QTest::newRow("<Hel(lo, )world>|words,rtl")
838 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
839 QTest::newRow("<Hel(lo)>,|words")
840 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
841 QTest::newRow("Hello<()>,|words")
842 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
843 QTest::newRow("Hello,<()>|words")
844 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
845 QTest::newRow("Hello<,( )>world|words,ltr")
846 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
847 QTest::newRow("Hello,<( )world>|words,rtl")
848 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
849 QTest::newRow("Hello<,( world)>|words,ltr")
850 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
851 QTest::newRow("Hello,<( world)>|words,rtl")
852 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
853 QTest::newRow("Hello<,( world!)>|words,ltr")
854 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
855 QTest::newRow("Hello,<( world!)>|words,rtl")
856 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
857 QTest::newRow("Hello<(, world!)>|words")
858 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
859 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
861 // QTest::newRow("world<(!)>|words")
862 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
863 QTest::newRow("world!<()>)|words")
864 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
865 QTest::newRow("world<()>!)|words")
866 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
868 QTest::newRow("<(,)>olleH |words")
869 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
870 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
871 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
872 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
873 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
874 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
875 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
876 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
877 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
878 QTest::newRow(",<(ol)leH>,|words")
879 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
880 QTest::newRow(",<()>olleH|words")
881 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
882 QTest::newRow("<()>,olleH|words")
883 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
884 QTest::newRow("<dlrow( )>,olleH|words,ltr")
885 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
886 QTest::newRow("dlrow<( ),>olleH|words,rtl")
887 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
888 QTest::newRow("<(dlrow )>,olleH|words,ltr")
889 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
890 QTest::newRow("<(dlrow ),>olleH|words,rtl")
891 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
892 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
893 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
894 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
895 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
896 QTest::newRow("(!dlrow ,)olleH|words")
897 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
898 QTest::newRow("<(!)>dlrow|words")
899 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
900 QTest::newRow("<()>!dlrow|words")
901 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
902 QTest::newRow("!<()>dlrow|words")
903 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
905 QTest::newRow(" <s(pac)ey> text |words")
906 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
907 QTest::newRow(" spacey <t(ex)t> |words")
908 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
909 QTest::newRow("<( )>spacey text |words|ltr")
910 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
911 QTest::newRow("<( )spacey> text |words|rtl")
912 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
913 QTest::newRow("spacey <text( )>|words|ltr")
914 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
916 // QTest::newRow("spacey text<( )>|words|rtl")
917 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
918 QTest::newRow("<()> spacey text |words")
919 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
920 QTest::newRow(" spacey text <()>|words")
921 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
924 void tst_qquicktextinput::moveCursorSelection()
926 QFETCH(QString, testStr);
927 QFETCH(int, cursorPosition);
928 QFETCH(int, movePosition);
929 QFETCH(QQuickTextInput::SelectionMode, mode);
930 QFETCH(int, selectionStart);
931 QFETCH(int, selectionEnd);
932 QFETCH(bool, reversible);
934 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
935 QQmlComponent textinputComponent(&engine);
936 textinputComponent.setData(componentStr.toLatin1(), QUrl());
937 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
938 QVERIFY(textinputObject != 0);
940 textinputObject->setCursorPosition(cursorPosition);
941 textinputObject->moveCursorSelection(movePosition, mode);
943 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
944 QCOMPARE(textinputObject->selectionStart(), selectionStart);
945 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
948 textinputObject->setCursorPosition(movePosition);
949 textinputObject->moveCursorSelection(cursorPosition, mode);
951 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
952 QCOMPARE(textinputObject->selectionStart(), selectionStart);
953 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
956 delete textinputObject;
959 void tst_qquicktextinput::moveCursorSelectionSequence_data()
961 QTest::addColumn<QString>("testStr");
962 QTest::addColumn<int>("cursorPosition");
963 QTest::addColumn<int>("movePosition1");
964 QTest::addColumn<int>("movePosition2");
965 QTest::addColumn<int>("selection1Start");
966 QTest::addColumn<int>("selection1End");
967 QTest::addColumn<int>("selection2Start");
968 QTest::addColumn<int>("selection2End");
970 // () contains the text selected by the cursor.
971 // <> contains the actual selection.
972 // ^ is the revised cursor position.
973 // {} contains the revised selection.
975 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
980 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
985 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
990 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
995 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1000 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1005 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1010 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1015 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1020 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1025 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1030 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1035 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1040 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1045 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1050 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1055 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1060 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1066 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1071 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1077 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1082 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1088 QTest::newRow("{<(^} sp)acey> text |ltr")
1093 QTest::newRow("{<( ^}sp)acey> text |ltr")
1098 QTest::newRow("<( {s^p)acey>} text |rtl")
1103 QTest::newRow("<( {^sp)acey>} text |rtl")
1109 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1115 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1117 // << 15 << 12 << 14
1120 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1126 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
1128 // << 12 << 15 << 14
1133 void tst_qquicktextinput::moveCursorSelectionSequence()
1135 QFETCH(QString, testStr);
1136 QFETCH(int, cursorPosition);
1137 QFETCH(int, movePosition1);
1138 QFETCH(int, movePosition2);
1139 QFETCH(int, selection1Start);
1140 QFETCH(int, selection1End);
1141 QFETCH(int, selection2Start);
1142 QFETCH(int, selection2End);
1144 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1145 QQmlComponent textinputComponent(&engine);
1146 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1147 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1148 QVERIFY(textinputObject != 0);
1150 textinputObject->setCursorPosition(cursorPosition);
1152 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1153 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1154 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1155 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1157 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1158 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1159 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1160 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1162 delete textinputObject;
1165 void tst_qquicktextinput::dragMouseSelection()
1167 QString qmlfile = testFile("mouseselection_true.qml");
1169 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1172 canvas.requestActivateWindow();
1173 QTest::qWaitForWindowShown(&canvas);
1175 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1177 QVERIFY(canvas.rootObject() != 0);
1178 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1179 QVERIFY(textInputObject != 0);
1181 // press-and-drag-and-release from x1 to x2
1184 int y = textInputObject->height()/2;
1185 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1186 QTest::mouseMove(&canvas, QPoint(x2, y));
1187 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1190 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1191 QVERIFY(str1.length() > 3);
1193 // press and drag the current selection.
1196 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1197 QTest::mouseMove(&canvas, QPoint(x2, y));
1198 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1200 QString str2 = textInputObject->selectedText();
1201 QVERIFY(str2.length() > 3);
1203 QVERIFY(str1 != str2);
1206 void tst_qquicktextinput::mouseSelectionMode_data()
1208 QTest::addColumn<QString>("qmlfile");
1209 QTest::addColumn<bool>("selectWords");
1210 QTest::addColumn<bool>("focus");
1211 QTest::addColumn<bool>("focusOnPress");
1214 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1215 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1216 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1217 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1218 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1219 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1220 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1221 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1222 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1225 void tst_qquicktextinput::mouseSelectionMode()
1227 QFETCH(QString, qmlfile);
1228 QFETCH(bool, selectWords);
1229 QFETCH(bool, focus);
1230 QFETCH(bool, focusOnPress);
1232 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1234 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1237 canvas.requestActivateWindow();
1238 QTest::qWaitForWindowShown(&canvas);
1239 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1241 QVERIFY(canvas.rootObject() != 0);
1242 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1243 QVERIFY(textInputObject != 0);
1245 textInputObject->setFocus(focus);
1246 textInputObject->setFocusOnPress(focusOnPress);
1248 // press-and-drag-and-release from x1 to x2
1251 int y = textInputObject->height()/2;
1252 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1253 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1254 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1257 QTRY_COMPARE(textInputObject->selectedText(), text);
1259 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1260 QVERIFY(textInputObject->selectedText() != text);
1264 void tst_qquicktextinput::horizontalAlignment_data()
1266 QTest::addColumn<int>("hAlign");
1267 QTest::addColumn<QString>("expectfile");
1269 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1270 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1271 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1274 void tst_qquicktextinput::horizontalAlignment()
1276 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1278 QFETCH(int, hAlign);
1279 QFETCH(QString, expectfile);
1281 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1284 canvas.requestActivateWindow();
1285 QTest::qWaitForWindowShown(&canvas);
1286 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1287 QObject *ob = canvas.rootObject();
1289 ob->setProperty("horizontalAlignment",hAlign);
1290 QImage actual = canvas.grabFrameBuffer();
1292 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1294 QImage expect(expectfile);
1296 QCOMPARE(actual,expect);
1299 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1301 PlatformInputContext platformInputContext;
1302 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1303 inputMethodPrivate->testContext = &platformInputContext;
1305 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1306 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1307 QVERIFY(textInput != 0);
1310 const QString rtlText = textInput->text();
1312 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1313 QVERIFY(textInputPrivate != 0);
1314 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1315 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1317 // implicit alignment should follow the reading direction of RTL text
1318 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1319 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1320 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1321 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1323 // explicitly left aligned
1324 textInput->setHAlign(QQuickTextInput::AlignLeft);
1325 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1326 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1327 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1329 // explicitly right aligned
1330 textInput->setHAlign(QQuickTextInput::AlignRight);
1331 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1332 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1333 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1334 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1336 // explicitly center aligned
1337 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1338 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1339 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1340 QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0);
1341 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width());
1343 // reseted alignment should go back to following the text reading direction
1344 textInput->resetHAlign();
1345 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1346 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1347 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1348 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1350 // mirror the text item
1351 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1353 // mirrored implicit alignment should continue to follow the reading direction of the text
1354 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1355 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1356 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1357 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1359 // explicitly right aligned behaves as left aligned
1360 textInput->setHAlign(QQuickTextInput::AlignRight);
1361 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1362 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1363 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1365 // mirrored explicitly left aligned behaves as right aligned
1366 textInput->setHAlign(QQuickTextInput::AlignLeft);
1367 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1368 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1369 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1370 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1372 // disable mirroring
1373 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1374 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1375 textInput->resetHAlign();
1377 // English text should be implicitly left aligned
1378 textInput->setText("Hello world!");
1379 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1380 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1382 canvas.requestActivateWindow();
1383 QTest::qWaitForWindowShown(&canvas);
1384 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1386 // If there is no commited text, the preedit text should determine the alignment.
1387 textInput->setText(QString());
1388 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1389 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1390 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1391 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1393 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1394 // redundant as an actual input method may take care of it.
1395 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1397 // empty text with implicit alignment follows the system locale-based
1398 // keyboard input direction from QInputMethod::inputDirection()
1399 textInput->setText("");
1400 platformInputContext.setInputDirection(Qt::LeftToRight);
1401 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1402 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1403 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1405 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1406 platformInputContext.setInputDirection(Qt::RightToLeft);
1407 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1408 QCOMPARE(cursorRectangleSpy.count(), 1);
1409 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1410 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1411 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1413 // set input direction while having content
1414 platformInputContext.setInputDirection(Qt::LeftToRight);
1415 textInput->setText("a");
1416 platformInputContext.setInputDirection(Qt::RightToLeft);
1417 QTest::keyClick(&canvas, Qt::Key_Backspace);
1418 QVERIFY(textInput->text().isEmpty());
1419 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1420 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1421 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1423 // input direction changed while not having focus
1424 platformInputContext.setInputDirection(Qt::LeftToRight);
1425 textInput->setFocus(false);
1426 platformInputContext.setInputDirection(Qt::RightToLeft);
1427 textInput->setFocus(true);
1428 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1429 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1430 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1432 textInput->setHAlign(QQuickTextInput::AlignRight);
1433 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1434 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1435 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1438 void tst_qquicktextinput::verticalAlignment()
1440 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1441 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1442 QVERIFY(textInput != 0);
1445 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1446 QVERIFY(textInputPrivate != 0);
1448 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1449 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2);
1450 QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
1451 QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
1454 textInput->setVAlign(QQuickTextInput::AlignBottom);
1455 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1456 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2);
1457 QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
1458 QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
1460 // explicitly center aligned
1461 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1462 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1463 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2);
1464 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2);
1465 QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
1466 QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
1467 QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
1468 QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
1471 void tst_qquicktextinput::boundingRect()
1473 QQmlComponent component(&engine);
1474 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1475 QScopedPointer<QObject> object(component.create());
1476 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1479 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1480 QCOMPARE(input->height(), input->boundingRect().height());
1482 input->setText("Hello World");
1483 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1484 QCOMPARE(input->height(), input->boundingRect().height());
1486 // bounding rect shouldn't exceed the size of the item, expect for the cursor width;
1487 input->setWidth(input->width() / 2);
1488 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1489 QCOMPARE(input->height(), input->boundingRect().height());
1491 input->setHeight(input->height() * 2);
1492 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1493 QCOMPARE(input->height(), input->boundingRect().height());
1495 QQmlComponent cursorComponent(&engine);
1496 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1498 input->setCursorDelegate(&cursorComponent);
1500 // If a cursor delegate is used it's size should determine the excess width.
1501 QCOMPARE(input->width() + 8, input->boundingRect().width());
1502 QCOMPARE(input->height(), input->boundingRect().height());
1505 void tst_qquicktextinput::positionAt()
1507 QQuickView canvas(testFileUrl("positionAt.qml"));
1508 QVERIFY(canvas.rootObject() != 0);
1510 canvas.requestActivateWindow();
1511 QTest::qWaitForWindowShown(&canvas);
1513 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1514 QVERIFY(textinputObject != 0);
1516 // Check autoscrolled...
1518 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1520 QTextLayout layout(textinputObject->text());
1521 layout.setFont(textinputObject->font());
1523 if (!qmlDisableDistanceField()) {
1525 option.setUseDesignMetrics(true);
1526 layout.setTextOption(option);
1528 layout.beginLayout();
1529 QTextLine line = layout.createLine();
1532 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1533 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1534 int textWidth = floor(line.horizontalAdvance());
1536 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1537 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1539 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1540 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1541 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1543 // Check without autoscroll...
1544 textinputObject->setAutoScroll(false);
1545 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1547 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1548 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1550 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1551 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1553 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1554 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1555 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1557 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1558 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1560 QString preeditText = textinputObject->text().mid(0, pos);
1561 textinputObject->setText(textinputObject->text().mid(pos));
1562 textinputObject->setCursorPosition(0);
1564 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1565 QVERIFY(qGuiApp->focusObject());
1566 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1568 // Check all points within the preedit text return the same position.
1569 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1570 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1571 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1573 // Verify positioning returns to normal after the preedit text.
1574 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1575 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1577 { QInputMethodEvent inputEvent;
1578 QVERIFY(qGuiApp->focusObject());
1579 QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1582 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1584 const qreal y0 = line.height() / 2;
1585 const qreal y1 = line.height() * 3 / 2;
1587 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1588 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1590 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1591 QVERIFY(newLinePos > pos);
1592 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1595 void tst_qquicktextinput::maxLength()
1597 QQuickView canvas(testFileUrl("maxLength.qml"));
1598 QVERIFY(canvas.rootObject() != 0);
1600 canvas.requestActivateWindow();
1601 QTest::qWaitForWindowShown(&canvas);
1603 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1604 QVERIFY(textinputObject != 0);
1605 QVERIFY(textinputObject->text().isEmpty());
1606 QVERIFY(textinputObject->maxLength() == 10);
1607 foreach (const QString &str, standard) {
1608 QVERIFY(textinputObject->text().length() <= 10);
1609 textinputObject->setText(str);
1610 QVERIFY(textinputObject->text().length() <= 10);
1613 textinputObject->setText("");
1614 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1615 for (int i=0; i<20; i++) {
1616 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1617 //simulateKey(&canvas, Qt::Key_A);
1618 QTest::keyPress(&canvas, Qt::Key_A);
1619 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1624 void tst_qquicktextinput::masks()
1626 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1627 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1628 QQuickView canvas(testFileUrl("masks.qml"));
1630 canvas.requestActivateWindow();
1631 QVERIFY(canvas.rootObject() != 0);
1632 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1633 QVERIFY(textinputObject != 0);
1634 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1635 QVERIFY(textinputObject->text().length() == 0);
1636 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1637 QCOMPARE(textinputObject->length(), 8);
1638 for (int i=0; i<10; i++) {
1639 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1640 QCOMPARE(textinputObject->length(), 8);
1641 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1642 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1643 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1644 //simulateKey(&canvas, Qt::Key_A);
1645 QTest::keyPress(&canvas, Qt::Key_A);
1646 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1651 void tst_qquicktextinput::validators()
1653 // Note that this test assumes that the validators are working properly
1654 // so you may need to run their tests first. All validators are checked
1655 // here to ensure that their exposure to QML is working.
1657 QLocale::setDefault(QLocale(QStringLiteral("C")));
1659 QQuickView canvas(testFileUrl("validators.qml"));
1661 canvas.requestActivateWindow();
1663 QVERIFY(canvas.rootObject() != 0);
1665 QLocale defaultLocale;
1666 QLocale enLocale("en");
1667 QLocale deLocale("de_DE");
1669 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1671 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1673 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1674 QVERIFY(intValidator);
1675 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1676 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1677 intValidator->setLocaleName(enLocale.name());
1678 QCOMPARE(intValidator->localeName(), enLocale.name());
1679 QCOMPARE(intInput->validator()->locale(), enLocale);
1680 intValidator->resetLocaleName();
1681 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1682 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1684 intInput->setFocus(true);
1685 QTRY_VERIFY(intInput->hasActiveFocus());
1686 QCOMPARE(intInput->hasAcceptableInput(), false);
1687 QCOMPARE(intInput->property("acceptable").toBool(), false);
1688 QTest::keyPress(&canvas, Qt::Key_1);
1689 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1691 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1692 QCOMPARE(intInput->hasAcceptableInput(), false);
1693 QCOMPARE(intInput->property("acceptable").toBool(), false);
1694 QCOMPARE(intSpy.count(), 0);
1695 QTest::keyPress(&canvas, Qt::Key_2);
1696 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1698 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1699 QCOMPARE(intInput->hasAcceptableInput(), false);
1700 QCOMPARE(intInput->property("acceptable").toBool(), false);
1701 QCOMPARE(intSpy.count(), 0);
1702 QTest::keyPress(&canvas, Qt::Key_Period);
1703 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1705 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1706 QCOMPARE(intInput->hasAcceptableInput(), false);
1707 QTest::keyPress(&canvas, Qt::Key_Comma);
1708 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1710 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1711 QCOMPARE(intInput->hasAcceptableInput(), false);
1712 QTest::keyPress(&canvas, Qt::Key_Backspace);
1713 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1715 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1716 QCOMPARE(intInput->hasAcceptableInput(), false);
1717 intValidator->setLocaleName(deLocale.name());
1718 QTest::keyPress(&canvas, Qt::Key_Period);
1719 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1721 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1722 QCOMPARE(intInput->hasAcceptableInput(), false);
1723 QTest::keyPress(&canvas, Qt::Key_Backspace);
1724 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1726 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1727 QCOMPARE(intInput->hasAcceptableInput(), false);
1728 intValidator->resetLocaleName();
1729 QTest::keyPress(&canvas, Qt::Key_1);
1730 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1732 QCOMPARE(intInput->text(), QLatin1String("11"));
1733 QCOMPARE(intInput->hasAcceptableInput(), true);
1734 QCOMPARE(intInput->property("acceptable").toBool(), true);
1735 QCOMPARE(intSpy.count(), 1);
1736 QTest::keyPress(&canvas, Qt::Key_0);
1737 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1739 QCOMPARE(intInput->text(), QLatin1String("11"));
1740 QCOMPARE(intInput->hasAcceptableInput(), true);
1741 QCOMPARE(intInput->property("acceptable").toBool(), true);
1742 QCOMPARE(intSpy.count(), 1);
1744 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1746 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1748 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1749 QVERIFY(dblValidator);
1750 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1751 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1752 dblValidator->setLocaleName(enLocale.name());
1753 QCOMPARE(dblValidator->localeName(), enLocale.name());
1754 QCOMPARE(dblInput->validator()->locale(), enLocale);
1755 dblValidator->resetLocaleName();
1756 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1757 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1759 dblInput->setFocus(true);
1760 QVERIFY(dblInput->hasActiveFocus() == true);
1761 QCOMPARE(dblInput->hasAcceptableInput(), false);
1762 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1763 QTest::keyPress(&canvas, Qt::Key_1);
1764 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1766 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1767 QCOMPARE(dblInput->hasAcceptableInput(), false);
1768 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1769 QCOMPARE(dblSpy.count(), 0);
1770 QTest::keyPress(&canvas, Qt::Key_2);
1771 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1773 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1774 QCOMPARE(dblInput->hasAcceptableInput(), true);
1775 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1776 QCOMPARE(dblSpy.count(), 1);
1777 QTest::keyPress(&canvas, Qt::Key_Comma);
1778 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1780 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1781 QCOMPARE(dblInput->hasAcceptableInput(), true);
1782 QTest::keyPress(&canvas, Qt::Key_1);
1783 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1785 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1786 QCOMPARE(dblInput->hasAcceptableInput(), true);
1787 dblValidator->setLocaleName(deLocale.name());
1788 QCOMPARE(dblInput->hasAcceptableInput(), true);
1789 QTest::keyPress(&canvas, Qt::Key_1);
1790 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1792 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1793 QCOMPARE(dblInput->hasAcceptableInput(), true);
1794 QTest::keyPress(&canvas, Qt::Key_1);
1795 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1797 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1798 QCOMPARE(dblInput->hasAcceptableInput(), true);
1799 QTest::keyPress(&canvas, Qt::Key_Backspace);
1800 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1802 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1803 QCOMPARE(dblInput->hasAcceptableInput(), true);
1804 QTest::keyPress(&canvas, Qt::Key_Backspace);
1805 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1807 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1808 QCOMPARE(dblInput->hasAcceptableInput(), true);
1809 QTest::keyPress(&canvas, Qt::Key_Backspace);
1810 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1812 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1813 QCOMPARE(dblInput->hasAcceptableInput(), true);
1814 dblValidator->resetLocaleName();
1815 QTest::keyPress(&canvas, Qt::Key_Period);
1816 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1818 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1819 QCOMPARE(dblInput->hasAcceptableInput(), true);
1820 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1821 QCOMPARE(dblSpy.count(), 1);
1822 QTest::keyPress(&canvas, Qt::Key_1);
1823 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1825 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1826 QCOMPARE(dblInput->hasAcceptableInput(), true);
1827 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1828 QCOMPARE(dblSpy.count(), 1);
1829 QTest::keyPress(&canvas, Qt::Key_1);
1830 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1832 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1833 QCOMPARE(dblInput->hasAcceptableInput(), true);
1834 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1835 QCOMPARE(dblSpy.count(), 1);
1836 QTest::keyPress(&canvas, Qt::Key_1);
1837 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1839 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1840 QCOMPARE(dblInput->hasAcceptableInput(), true);
1841 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1842 QCOMPARE(dblSpy.count(), 1);
1844 // Ensure the validator doesn't prevent characters being removed.
1845 dblInput->setValidator(intInput->validator());
1846 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1847 QCOMPARE(dblInput->hasAcceptableInput(), false);
1848 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1849 QCOMPARE(dblSpy.count(), 2);
1850 QTest::keyPress(&canvas, Qt::Key_Backspace);
1851 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1853 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1854 QCOMPARE(dblInput->hasAcceptableInput(), false);
1855 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1856 QCOMPARE(dblSpy.count(), 2);
1857 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1858 QTest::keyPress(&canvas, Qt::Key_1);
1859 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1861 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1862 QCOMPARE(dblInput->hasAcceptableInput(), false);
1863 QCOMPARE(dblSpy.count(), 2);
1864 QTest::keyPress(&canvas, Qt::Key_Backspace);
1865 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1867 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1868 QCOMPARE(dblInput->hasAcceptableInput(), false);
1869 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1870 QCOMPARE(dblSpy.count(), 2);
1871 QTest::keyPress(&canvas, Qt::Key_Backspace);
1872 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1874 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1875 QCOMPARE(dblInput->hasAcceptableInput(), false);
1876 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1877 QCOMPARE(dblSpy.count(), 2);
1878 QTest::keyPress(&canvas, Qt::Key_Backspace);
1879 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1881 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1882 QCOMPARE(dblInput->hasAcceptableInput(), false);
1883 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1884 QCOMPARE(dblSpy.count(), 2);
1885 QTest::keyPress(&canvas, Qt::Key_Backspace);
1886 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1888 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1889 QCOMPARE(dblInput->hasAcceptableInput(), false);
1890 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1891 QCOMPARE(dblSpy.count(), 2);
1892 QTest::keyPress(&canvas, Qt::Key_1);
1893 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1895 QCOMPARE(dblInput->text(), QLatin1String("11"));
1896 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1897 QCOMPARE(dblInput->hasAcceptableInput(), true);
1898 QCOMPARE(dblSpy.count(), 3);
1900 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1902 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
1903 strInput->setFocus(true);
1904 QVERIFY(strInput->hasActiveFocus() == true);
1905 QCOMPARE(strInput->hasAcceptableInput(), false);
1906 QCOMPARE(strInput->property("acceptable").toBool(), false);
1907 QTest::keyPress(&canvas, Qt::Key_1);
1908 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1910 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1911 QCOMPARE(strInput->hasAcceptableInput(), false);
1912 QCOMPARE(strInput->property("acceptable").toBool(), false);
1913 QCOMPARE(strSpy.count(), 0);
1914 QTest::keyPress(&canvas, Qt::Key_A);
1915 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1917 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1918 QCOMPARE(strInput->hasAcceptableInput(), false);
1919 QCOMPARE(strInput->property("acceptable").toBool(), false);
1920 QCOMPARE(strSpy.count(), 0);
1921 QTest::keyPress(&canvas, Qt::Key_A);
1922 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1924 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1925 QCOMPARE(strInput->hasAcceptableInput(), true);
1926 QCOMPARE(strInput->property("acceptable").toBool(), true);
1927 QCOMPARE(strSpy.count(), 1);
1928 QTest::keyPress(&canvas, Qt::Key_A);
1929 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1931 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1932 QCOMPARE(strInput->hasAcceptableInput(), true);
1933 QCOMPARE(strInput->property("acceptable").toBool(), true);
1934 QCOMPARE(strSpy.count(), 1);
1935 QTest::keyPress(&canvas, Qt::Key_A);
1936 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1938 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1939 QCOMPARE(strInput->hasAcceptableInput(), true);
1940 QCOMPARE(strInput->property("acceptable").toBool(), true);
1941 QCOMPARE(strSpy.count(), 1);
1942 QTest::keyPress(&canvas, Qt::Key_A);
1943 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1945 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1946 QCOMPARE(strInput->hasAcceptableInput(), true);
1947 QCOMPARE(strInput->property("acceptable").toBool(), true);
1948 QCOMPARE(strSpy.count(), 1);
1950 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
1951 QVERIFY(unvalidatedInput);
1952 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
1953 unvalidatedInput->setFocus(true);
1954 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
1955 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1956 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1957 QTest::keyPress(&canvas, Qt::Key_1);
1958 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1960 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
1961 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1962 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1963 QCOMPARE(unvalidatedSpy.count(), 0);
1964 QTest::keyPress(&canvas, Qt::Key_A);
1965 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1967 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
1968 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1969 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1970 QCOMPARE(unvalidatedSpy.count(), 0);
1973 void tst_qquicktextinput::inputMethods()
1975 QQuickView canvas(testFileUrl("inputmethods.qml"));
1977 canvas.requestActivateWindow();
1978 QTest::qWaitForWindowShown(&canvas);
1980 // test input method hints
1981 QVERIFY(canvas.rootObject() != 0);
1982 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1983 QVERIFY(input != 0);
1984 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1985 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
1986 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1987 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1988 QCOMPARE(inputMethodHintSpy.count(), 1);
1989 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1990 QCOMPARE(inputMethodHintSpy.count(), 1);
1993 QQuickTextInput plainInput;
1994 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
1996 input->setFocus(true);
1997 QVERIFY(input->hasActiveFocus() == true);
1998 // test that input method event is committed
1999 QInputMethodEvent event;
2000 event.setCommitString( "My ", -12, 0);
2001 QTRY_COMPARE(qGuiApp->focusObject(), input);
2002 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2003 QCOMPARE(input->text(), QString("My Hello world!"));
2005 input->setCursorPosition(2);
2006 event.setCommitString("Your", -2, 2);
2007 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2008 QCOMPARE(input->text(), QString("Your Hello world!"));
2009 QCOMPARE(input->cursorPosition(), 4);
2011 input->setCursorPosition(7);
2012 event.setCommitString("Goodbye", -2, 5);
2013 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2014 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2015 QCOMPARE(input->cursorPosition(), 12);
2017 input->setCursorPosition(8);
2018 event.setCommitString("Our", -8, 4);
2019 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2020 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2021 QCOMPARE(input->cursorPosition(), 7);
2023 // input should reset selection even if replacement parameters are out of bounds
2024 input->setText("text");
2025 input->setCursorPosition(0);
2026 input->moveCursorSelection(input->text().length());
2027 event.setCommitString("replacement", -input->text().length(), input->text().length());
2028 QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2029 QCOMPARE(input->selectionStart(), input->selectionEnd());
2031 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2032 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2033 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2035 input->setReadOnly(true);
2036 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2037 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2041 TextInput element should only handle left/right keys until the cursor reaches
2042 the extent of the text, then they should ignore the keys.
2045 void tst_qquicktextinput::navigation()
2047 QQuickView canvas(testFileUrl("navigation.qml"));
2049 canvas.requestActivateWindow();
2051 QVERIFY(canvas.rootObject() != 0);
2053 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2055 QVERIFY(input != 0);
2056 input->setCursorPosition(0);
2057 QTRY_VERIFY(input->hasActiveFocus() == true);
2058 simulateKey(&canvas, Qt::Key_Left);
2059 QVERIFY(input->hasActiveFocus() == false);
2060 simulateKey(&canvas, Qt::Key_Right);
2061 QVERIFY(input->hasActiveFocus() == true);
2062 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2063 input->setCursorPosition(input->text().length());
2064 input->select(0,input->text().length());
2065 QVERIFY(input->selectionStart() != input->selectionEnd());
2066 simulateKey(&canvas, Qt::Key_Right);
2067 QVERIFY(input->selectionStart() == input->selectionEnd());
2068 QVERIFY(input->selectionStart() == input->text().length());
2069 QVERIFY(input->hasActiveFocus() == true);
2070 simulateKey(&canvas, Qt::Key_Right);
2071 QVERIFY(input->hasActiveFocus() == false);
2072 simulateKey(&canvas, Qt::Key_Left);
2073 QVERIFY(input->hasActiveFocus() == true);
2075 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2076 input->setCursorPosition(2);
2077 QCOMPARE(input->cursorPosition(),2);
2078 simulateKey(&canvas, Qt::Key_Up);
2079 QCOMPARE(input->cursorPosition(),2);
2080 simulateKey(&canvas, Qt::Key_Down);
2081 QCOMPARE(input->cursorPosition(),2);
2083 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2084 input->setText(QString());
2085 QCOMPARE(input->cursorPosition(), 0);
2086 simulateKey(&canvas, Qt::Key_Right);
2087 QCOMPARE(input->hasActiveFocus(), false);
2088 simulateKey(&canvas, Qt::Key_Left);
2089 QCOMPARE(input->hasActiveFocus(), true);
2090 simulateKey(&canvas, Qt::Key_Left);
2091 QCOMPARE(input->hasActiveFocus(), false);
2094 void tst_qquicktextinput::navigation_RTL()
2096 QQuickView canvas(testFileUrl("navigation.qml"));
2098 canvas.requestActivateWindow();
2100 QVERIFY(canvas.rootObject() != 0);
2102 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2104 QVERIFY(input != 0);
2105 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2106 input->setText(QString::fromUtf16(arabic_str, 11));
2108 input->setCursorPosition(0);
2109 QTRY_VERIFY(input->hasActiveFocus() == true);
2112 simulateKey(&canvas, Qt::Key_Right);
2113 QVERIFY(input->hasActiveFocus() == false);
2116 simulateKey(&canvas, Qt::Key_Left);
2117 QVERIFY(input->hasActiveFocus() == true);
2119 input->setCursorPosition(input->text().length());
2120 QVERIFY(input->hasActiveFocus() == true);
2123 simulateKey(&canvas, Qt::Key_Left);
2124 QVERIFY(input->hasActiveFocus() == false);
2127 simulateKey(&canvas, Qt::Key_Right);
2128 QVERIFY(input->hasActiveFocus() == true);
2131 void tst_qquicktextinput::copyAndPaste() {
2132 #ifndef QT_NO_CLIPBOARD
2136 PasteboardRef pasteboard;
2137 OSStatus status = PasteboardCreate(0, &pasteboard);
2138 if (status == noErr)
2139 CFRelease(pasteboard);
2141 QSKIP("This machine doesn't support the clipboard");
2145 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2146 QQmlComponent textInputComponent(&engine);
2147 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2148 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2149 QVERIFY(textInput != 0);
2152 QCOMPARE(textInput->text().length(), 12);
2153 textInput->select(0, textInput->text().length());
2155 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2156 QCOMPARE(textInput->selectedText().length(), 12);
2157 textInput->setCursorPosition(0);
2158 QVERIFY(textInput->canPaste());
2160 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2161 QCOMPARE(textInput->text().length(), 24);
2164 QVERIFY(textInput->canPaste());
2165 textInput->setReadOnly(true);
2166 QVERIFY(!textInput->canPaste());
2167 textInput->setReadOnly(false);
2168 QVERIFY(textInput->canPaste());
2171 textInput->setCursorPosition(0);
2172 textInput->selectWord();
2173 QCOMPARE(textInput->selectedText(), QString("Hello"));
2175 // select all and cut
2176 textInput->selectAll();
2178 QCOMPARE(textInput->text().length(), 0);
2180 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2181 QCOMPARE(textInput->text().length(), 24);
2183 // clear copy buffer
2184 QClipboard *clipboard = QGuiApplication::clipboard();
2187 QVERIFY(!textInput->canPaste());
2189 // test that copy functionality is disabled
2190 // when echo mode is set to hide text/password mode
2193 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2194 textInput->setEchoMode(echoMode);
2195 textInput->setText("My password");
2196 textInput->select(0, textInput->text().length());
2198 if (echoMode == QQuickTextInput::Normal) {
2199 QVERIFY(!clipboard->text().isEmpty());
2200 QCOMPARE(clipboard->text(), QString("My password"));
2203 QVERIFY(clipboard->text().isEmpty());
2212 void tst_qquicktextinput::copyAndPasteKeySequence() {
2213 #ifndef QT_NO_CLIPBOARD
2217 PasteboardRef pasteboard;
2218 OSStatus status = PasteboardCreate(0, &pasteboard);
2219 if (status == noErr)
2220 CFRelease(pasteboard);
2222 QSKIP("This machine doesn't support the clipboard");
2226 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2227 QQmlComponent textInputComponent(&engine);
2228 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2229 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2230 QVERIFY(textInput != 0);
2232 QQuickCanvas canvas;
2233 textInput->setParentItem(canvas.rootItem());
2235 canvas.requestActivateWindow();
2236 QTest::qWaitForWindowShown(&canvas);
2237 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2240 QVERIFY(textInput->hasActiveFocus());
2241 QCOMPARE(textInput->text().length(), 12);
2242 textInput->select(0, textInput->text().length());
2243 simulateKeys(&canvas, QKeySequence::Copy);
2244 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2245 QCOMPARE(textInput->selectedText().length(), 12);
2246 textInput->setCursorPosition(0);
2247 QVERIFY(textInput->canPaste());
2248 simulateKeys(&canvas, QKeySequence::Paste);
2249 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2250 QCOMPARE(textInput->text().length(), 24);
2252 // select all and cut
2253 simulateKeys(&canvas, QKeySequence::SelectAll);
2254 simulateKeys(&canvas, QKeySequence::Cut);
2255 QCOMPARE(textInput->text().length(), 0);
2256 simulateKeys(&canvas, QKeySequence::Paste);
2257 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2258 QCOMPARE(textInput->text().length(), 24);
2260 // clear copy buffer
2261 QClipboard *clipboard = QGuiApplication::clipboard();
2264 QVERIFY(!textInput->canPaste());
2266 // test that copy functionality is disabled
2267 // when echo mode is set to hide text/password mode
2270 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2271 textInput->setEchoMode(echoMode);
2272 textInput->setText("My password");
2273 textInput->select(0, textInput->text().length());
2274 simulateKeys(&canvas, QKeySequence::Copy);
2275 if (echoMode == QQuickTextInput::Normal) {
2276 QVERIFY(!clipboard->text().isEmpty());
2277 QCOMPARE(clipboard->text(), QString("My password"));
2280 QVERIFY(clipboard->text().isEmpty());
2289 void tst_qquicktextinput::canPasteEmpty() {
2290 #ifndef QT_NO_CLIPBOARD
2292 QGuiApplication::clipboard()->clear();
2294 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2295 QQmlComponent textInputComponent(&engine);
2296 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2297 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2298 QVERIFY(textInput != 0);
2300 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2301 QCOMPARE(textInput->canPaste(), cp);
2306 void tst_qquicktextinput::canPaste() {
2307 #ifndef QT_NO_CLIPBOARD
2309 QGuiApplication::clipboard()->setText("Some text");
2311 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2312 QQmlComponent textInputComponent(&engine);
2313 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2314 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2315 QVERIFY(textInput != 0);
2317 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2318 QCOMPARE(textInput->canPaste(), cp);
2323 void tst_qquicktextinput::passwordCharacter()
2325 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2326 QQmlComponent textInputComponent(&engine);
2327 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2328 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2329 QVERIFY(textInput != 0);
2331 textInput->setPasswordCharacter("X");
2332 qreal implicitWidth = textInput->implicitWidth();
2333 textInput->setPasswordCharacter(".");
2335 // QTBUG-12383 content is updated and redrawn
2336 QVERIFY(textInput->implicitWidth() < implicitWidth);
2341 void tst_qquicktextinput::cursorDelegate_data()
2343 QTest::addColumn<QUrl>("source");
2344 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2345 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2346 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2349 void tst_qquicktextinput::cursorDelegate()
2351 QFETCH(QUrl, source);
2352 QQuickView view(source);
2354 view.requestActivateWindow();
2355 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2356 QVERIFY(textInputObject != 0);
2357 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2358 //Test Delegate gets created
2359 textInputObject->setFocus(true);
2360 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2361 QVERIFY(delegateObject);
2362 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2363 //Test Delegate gets moved
2364 for (int i=0; i<= textInputObject->text().length(); i++) {
2365 textInputObject->setCursorPosition(i);
2366 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2367 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2369 textInputObject->setCursorPosition(0);
2370 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2371 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2374 // Test delegate gets moved on mouse press.
2375 textInputObject->setSelectByMouse(true);
2376 textInputObject->setCursorPosition(0);
2377 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2378 QTest::qWait(400); //ensure this isn't treated as a double-click
2379 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2381 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2382 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2383 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2385 // Test delegate gets moved on mouse drag
2386 textInputObject->setCursorPosition(0);
2387 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2388 QTest::qWait(400); //ensure this isn't treated as a double-click
2389 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2390 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2391 QGuiApplication::sendEvent(&view, &mv);
2392 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2394 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2395 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2397 textInputObject->setReadOnly(true);
2398 textInputObject->setCursorPosition(0);
2399 QTest::qWait(400); //ensure this isn't treated as a double-click
2400 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2402 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2403 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2404 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2406 textInputObject->setCursorPosition(0);
2407 QTest::qWait(400); //ensure this isn't treated as a double-click
2408 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2410 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2411 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2412 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2414 textInputObject->setCursorPosition(0);
2415 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2416 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2418 // Delegate moved when text is entered
2419 textInputObject->setText(QString());
2420 for (int i = 0; i < 20; ++i) {
2421 QTest::keyClick(&view, Qt::Key_A);
2422 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2423 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2426 // Delegate moved when text is entered by im.
2427 textInputObject->setText(QString());
2428 for (int i = 0; i < 20; ++i) {
2429 QInputMethodEvent event;
2430 event.setCommitString("a");
2431 QGuiApplication::sendEvent(&view, &event);
2432 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2433 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2436 //Test Delegate gets deleted
2437 textInputObject->setCursorDelegate(0);
2438 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2441 void tst_qquicktextinput::cursorVisible()
2443 QQuickTextInput input;
2444 input.componentComplete();
2445 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2447 QQuickView view(testFileUrl("cursorVisible.qml"));
2449 view.requestActivateWindow();
2450 QTest::qWaitForWindowShown(&view);
2451 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2453 QCOMPARE(input.isCursorVisible(), false);
2455 input.setCursorVisible(true);
2456 QCOMPARE(input.isCursorVisible(), true);
2457 QCOMPARE(spy.count(), 1);
2459 input.setCursorVisible(false);
2460 QCOMPARE(input.isCursorVisible(), false);
2461 QCOMPARE(spy.count(), 2);
2463 input.setFocus(true);
2464 QCOMPARE(input.isCursorVisible(), false);
2465 QCOMPARE(spy.count(), 2);
2467 input.setParentItem(view.rootObject());
2468 QCOMPARE(input.isCursorVisible(), true);
2469 QCOMPARE(spy.count(), 3);
2471 input.setFocus(false);
2472 QCOMPARE(input.isCursorVisible(), false);
2473 QCOMPARE(spy.count(), 4);
2475 input.setFocus(true);
2476 QCOMPARE(input.isCursorVisible(), true);
2477 QCOMPARE(spy.count(), 5);
2479 QWindow alternateView;
2480 alternateView.show();
2481 alternateView.requestActivateWindow();
2482 QTest::qWaitForWindowShown(&alternateView);
2484 QCOMPARE(input.isCursorVisible(), false);
2485 QCOMPARE(spy.count(), 6);
2487 view.requestActivateWindow();
2488 QTest::qWaitForWindowShown(&view);
2489 QCOMPARE(input.isCursorVisible(), true);
2490 QCOMPARE(spy.count(), 7);
2492 { // Cursor attribute with 0 length hides cursor.
2493 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2494 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2495 QCoreApplication::sendEvent(&input, &ev);
2497 QCOMPARE(input.isCursorVisible(), false);
2498 QCOMPARE(spy.count(), 8);
2500 { // Cursor attribute with non zero length shows cursor.
2501 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2502 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2503 QCoreApplication::sendEvent(&input, &ev);
2505 QCOMPARE(input.isCursorVisible(), true);
2506 QCOMPARE(spy.count(), 9);
2508 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2509 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2510 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2511 QCoreApplication::sendEvent(&input, &ev);
2513 QCOMPARE(input.isCursorVisible(), false);
2514 QCOMPARE(spy.count(), 10);
2516 input.setText("something");
2517 QCOMPARE(input.isCursorVisible(), true);
2518 QCOMPARE(spy.count(), 11);
2520 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2521 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2522 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2523 QCoreApplication::sendEvent(&input, &ev);
2525 QCOMPARE(input.isCursorVisible(), false);
2526 QCOMPARE(spy.count(), 12);
2528 input.setCursorPosition(5);
2529 QCOMPARE(input.isCursorVisible(), true);
2530 QCOMPARE(spy.count(), 13);
2533 void tst_qquicktextinput::cursorRectangle_data()
2535 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2537 QTest::addColumn<QString>("text");
2538 QTest::addColumn<int>("positionAtWidth");
2539 QTest::addColumn<int>("wrapPosition");
2540 QTest::addColumn<QString>("shortText");
2541 QTest::addColumn<bool>("leftToRight");
2543 QTest::newRow("left to right")
2544 << "Hello World!" << 5 << 11
2547 QTest::newRow("right to left")
2548 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2549 << QString::fromUtf16(arabic_str, 3)
2553 void tst_qquicktextinput::cursorRectangle()
2556 QFETCH(QString, text);
2557 QFETCH(int, positionAtWidth);
2558 QFETCH(int, wrapPosition);
2559 QFETCH(QString, shortText);
2560 QFETCH(bool, leftToRight);
2562 QQuickTextInput input;
2563 input.setText(text);
2564 input.componentComplete();
2566 QTextLayout layout(text);
2567 layout.setFont(input.font());
2568 if (!qmlDisableDistanceField()) {
2570 option.setUseDesignMetrics(true);
2571 layout.setTextOption(option);
2573 layout.beginLayout();
2574 QTextLine line = layout.createLine();
2579 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2581 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2582 offset = line.horizontalAdvance() - input.width();
2584 input.setHeight(qCeil(line.height() * 3 / 2));
2588 for (int i = 0; i <= positionAtWidth; ++i) {
2589 input.setCursorPosition(i);
2590 r = input.cursorRectangle();
2592 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2593 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2594 QCOMPARE(input.positionToRectangle(i), r);
2597 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2598 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2600 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2601 input.setCursorPosition(i);
2602 QCOMPARE(r, input.cursorRectangle());
2603 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2604 QCOMPARE(input.positionToRectangle(i), r);
2607 for (int i = text.length() - 2; i >= 0; --i) {
2608 input.setCursorPosition(i);
2609 r = input.cursorRectangle();
2610 QCOMPARE(r.top(), 0.);
2612 QVERIFY(r.right() >= 0);
2614 QVERIFY(r.left() <= input.width());
2616 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2617 QCOMPARE(input.positionToRectangle(i), r);
2620 // Check position with word wrap.
2621 input.setWrapMode(QQuickTextInput::WordWrap);
2622 input.setAutoScroll(false);
2623 for (int i = 0; i < wrapPosition; ++i) {
2624 input.setCursorPosition(i);
2625 r = input.cursorRectangle();
2627 if (i > positionAtWidth)
2628 QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2629 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2630 QCOMPARE(r.top(), 0.);
2631 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2632 QCOMPARE(input.positionToRectangle(i), r);
2635 input.setCursorPosition(wrapPosition);
2636 r = input.cursorRectangle();
2638 QCOMPARE(r.left(), 0.);
2640 QCOMPARE(r.left(), input.width());
2642 QVERIFY(r.top() >= line.height() - 1);
2643 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2644 QCOMPARE(input.positionToRectangle(11), r);
2646 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2647 input.setCursorPosition(i);
2648 r = input.cursorRectangle();
2649 QVERIFY(r.top() >= line.height() - 1);
2650 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2651 QCOMPARE(input.positionToRectangle(i), r);
2654 // Check vertical scrolling with word wrap.
2655 input.setAutoScroll(true);
2656 for (int i = 0; i <= positionAtWidth; ++i) {
2657 input.setCursorPosition(i);
2658 r = input.cursorRectangle();
2660 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2661 QCOMPARE(r.top(), 0.);
2662 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2663 QCOMPARE(input.positionToRectangle(i), r);
2666 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
2667 // reaches the next non-whitespace character.
2668 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2669 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
2670 input.setCursorPosition(i);
2671 QCOMPARE(r, input.cursorRectangle());
2672 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2673 QCOMPARE(input.positionToRectangle(i), r);
2676 input.setCursorPosition(wrapPosition);
2677 r = input.cursorRectangle();
2679 QCOMPARE(r.left(), 0.);
2681 QCOMPARE(r.left(), input.width());
2683 QVERIFY(r.bottom() >= input.height());
2684 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2685 QCOMPARE(input.positionToRectangle(11), r);
2687 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2688 input.setCursorPosition(i);
2689 r = input.cursorRectangle();
2690 QVERIFY(r.bottom() >= input.height());
2691 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2692 QCOMPARE(input.positionToRectangle(i), r);
2695 for (int i = text.length() - 2; i >= wrapPosition; --i) {
2696 input.setCursorPosition(i);
2697 r = input.cursorRectangle();
2698 QVERIFY(r.bottom() >= input.height());
2699 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2700 QCOMPARE(input.positionToRectangle(i), r);
2703 input.setCursorPosition(wrapPosition - 1);
2704 r = input.cursorRectangle();
2705 QCOMPARE(r.top(), 0.);
2706 QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2707 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2708 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2709 QCOMPARE(input.positionToRectangle(10), r);
2711 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
2712 input.setCursorPosition(i);
2713 QCOMPARE(r, input.cursorRectangle());
2714 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2715 QCOMPARE(input.positionToRectangle(i), r);
2718 for (int i = positionAtWidth; i >= 0; --i) {
2719 input.setCursorPosition(i);
2720 r = input.cursorRectangle();
2721 QCOMPARE(r.top(), 0.);
2722 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2723 QCOMPARE(input.positionToRectangle(i), r);
2726 input.setText(shortText);
2727 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
2728 r = input.cursorRectangle();
2729 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2732 void tst_qquicktextinput::readOnly()
2734 QQuickView canvas(testFileUrl("readOnly.qml"));
2736 canvas.requestActivateWindow();
2738 QVERIFY(canvas.rootObject() != 0);
2740 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2742 QVERIFY(input != 0);
2743 QTRY_VERIFY(input->hasActiveFocus() == true);
2744 QVERIFY(input->isReadOnly() == true);
2745 QString initial = input->text();
2746 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2747 simulateKey(&canvas, k);
2748 simulateKey(&canvas, Qt::Key_Return);
2749 simulateKey(&canvas, Qt::Key_Space);
2750 simulateKey(&canvas, Qt::Key_Escape);
2751 QCOMPARE(input->text(), initial);
2753 input->setCursorPosition(3);
2754 input->setReadOnly(false);
2755 QCOMPARE(input->isReadOnly(), false);
2756 QCOMPARE(input->cursorPosition(), input->text().length());
2759 void tst_qquicktextinput::echoMode()
2761 QQuickView canvas(testFileUrl("echoMode.qml"));
2763 canvas.requestActivateWindow();
2764 QTest::qWaitForWindowShown(&canvas);
2765 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2767 QVERIFY(canvas.rootObject() != 0);
2769 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2771 QVERIFY(input != 0);
2772 QTRY_VERIFY(input->hasActiveFocus() == true);
2773 QString initial = input->text();
2774 Qt::InputMethodHints ref;
2775 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2776 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2777 QCOMPARE(input->displayText(), input->text());
2779 ref &= ~Qt::ImhHiddenText;
2780 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2781 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2782 input->setEchoMode(QQuickTextInput::NoEcho);
2783 QCOMPARE(input->text(), initial);
2784 QCOMPARE(input->displayText(), QLatin1String(""));
2785 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2787 ref |= Qt::ImhHiddenText;
2788 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2789 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2790 input->setEchoMode(QQuickTextInput::Password);
2792 ref |= Qt::ImhHiddenText;
2793 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2794 QCOMPARE(input->text(), initial);
2795 QCOMPARE(input->displayText(), QLatin1String("********"));
2796 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2797 // clearing input hints do not clear bits set by echo mode
2798 input->setInputMethodHints(Qt::ImhNone);
2799 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2800 input->setPasswordCharacter(QChar('Q'));
2801 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2802 QCOMPARE(input->text(), initial);
2803 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2804 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2805 //PasswordEchoOnEdit
2806 ref &= ~Qt::ImhHiddenText;
2807 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2808 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2809 QCOMPARE(input->text(), initial);
2810 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2811 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2812 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2813 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2814 QCOMPARE(input->text(), QLatin1String("a"));
2815 QCOMPARE(input->displayText(), QLatin1String("a"));
2816 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2817 input->setFocus(false);
2818 QVERIFY(input->hasActiveFocus() == false);
2819 QCOMPARE(input->displayText(), QLatin1String("Q"));
2820 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2821 input->setFocus(true);
2822 QVERIFY(input->hasActiveFocus());
2823 QInputMethodEvent inputEvent;
2824 inputEvent.setCommitString(initial);
2825 QGuiApplication::sendEvent(input, &inputEvent);
2826 QCOMPARE(input->text(), initial);
2827 QCOMPARE(input->displayText(), initial);
2828 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2831 void tst_qquicktextinput::passwordEchoDelay()
2833 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
2835 QSKIP("No mask delay in use");
2836 QQuickView canvas(testFileUrl("echoMode.qml"));
2838 canvas.requestActivateWindow();
2839 QTest::qWaitForWindowShown(&canvas);
2840 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2842 QVERIFY(canvas.rootObject() != 0);
2844 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2846 QChar fillChar = QLatin1Char('*');
2848 input->setEchoMode(QQuickTextInput::Password);
2849 QCOMPARE(input->displayText(), QString(8, fillChar));
2850 input->setText(QString());
2851 QCOMPARE(input->displayText(), QString());
2853 QTest::keyPress(&canvas, '0');
2854 QTest::keyPress(&canvas, '1');
2855 QTest::keyPress(&canvas, '2');
2856 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2857 QTest::keyPress(&canvas, '3');
2858 QTest::keyPress(&canvas, '4');
2859 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2860 QTest::keyPress(&canvas, Qt::Key_Backspace);
2861 QCOMPARE(input->displayText(), QString(4, fillChar));
2862 QTest::keyPress(&canvas, '4');
2863 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2864 QTest::qWait(maskDelay);
2865 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2866 QTest::keyPress(&canvas, '5');
2867 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2868 input->setFocus(false);
2869 QVERIFY(!input->hasFocus());
2870 QCOMPARE(input->displayText(), QString(6, fillChar));
2871 input->setFocus(true);
2872 QTRY_VERIFY(input->hasFocus());
2873 QCOMPARE(input->displayText(), QString(6, fillChar));
2874 QTest::keyPress(&canvas, '6');
2875 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2877 QInputMethodEvent ev;
2878 ev.setCommitString(QLatin1String("7"));
2879 QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev);
2880 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2882 input->setCursorPosition(3);
2883 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2884 QTest::keyPress(&canvas, 'a');
2885 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2886 QTest::keyPress(&canvas, Qt::Key_Backspace);
2887 QCOMPARE(input->displayText(), QString(8, fillChar));
2891 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
2893 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2894 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2896 QGuiApplication::sendEvent(view, &press);
2897 QGuiApplication::sendEvent(view, &release);
2901 void tst_qquicktextinput::focusOnPress()
2903 QString componentStr =
2904 "import QtQuick 2.0\n"
2906 "property bool selectOnFocus: false\n"
2907 "width: 100; height: 50\n"
2908 "activeFocusOnPress: true\n"
2909 "text: \"Hello World\"\n"
2910 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
2912 QQmlComponent texteditComponent(&engine);
2913 texteditComponent.setData(componentStr.toLatin1(), QUrl());
2914 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
2915 QVERIFY(textInputObject != 0);
2916 QCOMPARE(textInputObject->focusOnPress(), true);
2917 QCOMPARE(textInputObject->hasFocus(), false);
2919 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
2920 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
2921 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
2923 textInputObject->setFocusOnPress(true);
2924 QCOMPARE(textInputObject->focusOnPress(), true);
2925 QCOMPARE(activeFocusOnPressSpy.count(), 0);
2927 QQuickCanvas canvas;
2928 canvas.resize(100, 50);
2929 textInputObject->setParentItem(canvas.rootItem());
2931 canvas.requestActivateWindow();
2932 QTest::qWaitForWindowShown(&canvas);
2933 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2935 QCOMPARE(textInputObject->hasFocus(), false);
2936 QCOMPARE(textInputObject->hasActiveFocus(), false);
2938 QPoint centerPoint(canvas.width()/2, canvas.height()/2);
2939 Qt::KeyboardModifiers noModifiers = 0;
2940 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2941 QGuiApplication::processEvents();
2942 QCOMPARE(textInputObject->hasFocus(), true);
2943 QCOMPARE(textInputObject->hasActiveFocus(), true);
2944 QCOMPARE(focusSpy.count(), 1);
2945 QCOMPARE(activeFocusSpy.count(), 1);
2946 QCOMPARE(textInputObject->selectedText(), QString());
2947 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2949 textInputObject->setFocusOnPress(false);
2950 QCOMPARE(textInputObject->focusOnPress(), false);
2951 QCOMPARE(activeFocusOnPressSpy.count(), 1);
2953 textInputObject->setFocus(false);
2954 QCOMPARE(textInputObject->hasFocus(), false);
2955 QCOMPARE(textInputObject->hasActiveFocus(), false);
2956 QCOMPARE(focusSpy.count(), 2);
2957 QCOMPARE(activeFocusSpy.count(), 2);
2959 // Wait for double click timeout to expire before clicking again.
2961 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2962 QGuiApplication::processEvents();
2963 QCOMPARE(textInputObject->hasFocus(), false);
2964 QCOMPARE(textInputObject->hasActiveFocus(), false);
2965 QCOMPARE(focusSpy.count(), 2);
2966 QCOMPARE(activeFocusSpy.count(), 2);
2967 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2969 textInputObject->setFocusOnPress(true);
2970 QCOMPARE(textInputObject->focusOnPress(), true);
2971 QCOMPARE(activeFocusOnPressSpy.count(), 2);
2973 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
2974 textInputObject->setProperty("selectOnFocus", true);
2977 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2978 QGuiApplication::processEvents();
2979 QCOMPARE(textInputObject->hasFocus(), true);
2980 QCOMPARE(textInputObject->hasActiveFocus(), true);
2981 QCOMPARE(focusSpy.count(), 3);
2982 QCOMPARE(activeFocusSpy.count(), 3);
2983 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
2984 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
2987 void tst_qquicktextinput::openInputPanel()
2989 PlatformInputContext platformInputContext;
2990 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2991 inputMethodPrivate->testContext = &platformInputContext;
2993 QQuickView view(testFileUrl("openInputPanel.qml"));
2995 view.requestActivateWindow();
2996 QTest::qWaitForWindowShown(&view);
2997 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2999 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3002 // check default values
3003 QVERIFY(input->focusOnPress());
3004 QVERIFY(!input->hasActiveFocus());
3005 QVERIFY(qApp->focusObject() != input);
3006 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3008 // input panel should open on focus
3009 QPoint centerPoint(view.width()/2, view.height()/2);
3010 Qt::KeyboardModifiers noModifiers = 0;
3011 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3012 QGuiApplication::processEvents();
3013 QVERIFY(input->hasActiveFocus());
3014 QCOMPARE(qApp->focusObject(), input);
3015 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3016 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3018 // input panel should be re-opened when pressing already focused TextInput
3019 qApp->inputMethod()->hide();
3020 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3021 QVERIFY(input->hasActiveFocus());
3022 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3023 QGuiApplication::processEvents();
3024 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3025 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3027 // input panel should stay visible if focus is lost to another text inputor
3028 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3029 QQuickTextInput anotherInput;
3030 anotherInput.componentComplete();
3031 anotherInput.setParentItem(view.rootObject());
3032 anotherInput.setFocus(true);
3033 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3034 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3035 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3037 anotherInput.setFocus(false);
3038 QVERIFY(qApp->focusObject() != &anotherInput);
3039 QCOMPARE(view.activeFocusItem(), view.rootItem());
3040 anotherInput.setFocus(true);
3042 qApp->inputMethod()->hide();
3044 // input panel should not be opened if TextInput is read only
3045 input->setReadOnly(true);
3046 input->setFocus(true);
3047 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3048 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3049 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3050 QGuiApplication::processEvents();
3051 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3053 // input panel should not be opened if focusOnPress is set to false
3054 input->setFocusOnPress(false);
3055 input->setFocus(false);
3056 input->setFocus(true);
3057 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3058 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3059 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3060 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3062 // input panel should open when openSoftwareInputPanel is called
3063 input->openSoftwareInputPanel();
3064 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3066 // input panel should close when closeSoftwareInputPanel is called
3067 input->closeSoftwareInputPanel();
3068 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3071 class MyTextInput : public QQuickTextInput
3074 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3078 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3081 return QQuickTextInput::updatePaintNode(node, data);
3086 void tst_qquicktextinput::setHAlignClearCache()
3090 input.setText("Hello world");
3091 input.setParentItem(view.rootItem());
3093 view.requestActivateWindow();
3094 QTest::qWaitForWindowShown(&view);
3096 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3098 QTRY_COMPARE(input.nbPaint, 1);
3099 input.setHAlign(QQuickTextInput::AlignRight);
3100 //Changing the alignment should trigger a repaint
3101 QTRY_COMPARE(input.nbPaint, 2);
3104 void tst_qquicktextinput::focusOutClearSelection()
3107 QQuickTextInput input;
3108 QQuickTextInput input2;
3109 input.setText(QLatin1String("Hello world"));
3110 input.setFocus(true);
3111 input2.setParentItem(view.rootItem());
3112 input.setParentItem(view.rootItem());
3113 input.componentComplete();
3114 input2.componentComplete();
3116 view.requestActivateWindow();
3117 QTest::qWaitForWindowShown(&view);
3119 //The selection should work
3120 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3121 input2.setFocus(true);
3122 QGuiApplication::processEvents();
3123 //The input lost the focus selection should be cleared
3124 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3127 void tst_qquicktextinput::geometrySignals()
3129 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3130 QObject *o = component.create();
3132 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3133 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3137 void tst_qquicktextinput::contentSize()
3139 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3140 QQmlComponent textComponent(&engine);
3141 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3142 QScopedPointer<QObject> object(textComponent.create());
3143 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3145 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3147 textObject->setText("The quick red fox jumped over the lazy brown dog");
3149 QVERIFY(textObject->contentWidth() > textObject->width());
3150 QVERIFY(textObject->contentHeight() < textObject->height());
3151 QCOMPARE(spy.count(), 1);
3153 textObject->setWrapMode(QQuickTextInput::WordWrap);
3154 QVERIFY(textObject->contentWidth() <= textObject->width());
3155 QVERIFY(textObject->contentHeight() > textObject->height());
3156 QCOMPARE(spy.count(), 2);
3158 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3160 QVERIFY(textObject->contentWidth() > textObject->width());
3161 QVERIFY(textObject->contentHeight() > textObject->height());
3162 QCOMPARE(spy.count(), 3);
3164 textObject->setText("The quick red fox jumped over the lazy brown dog");
3165 for (int w = 60; w < 120; ++w) {
3166 textObject->setWidth(w);
3167 QVERIFY(textObject->contentWidth() <= textObject->width());
3168 QVERIFY(textObject->contentHeight() > textObject->height());
3172 static void sendPreeditText(const QString &text, int cursor)
3174 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3175 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3176 QCoreApplication::sendEvent(qGuiApp->focusObject(), &event);
3179 void tst_qquicktextinput::preeditAutoScroll()
3181 QString preeditText = "califragisiticexpialidocious!";
3183 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3185 view.requestActivateWindow();
3186 QTest::qWaitForWindowShown(&view);
3187 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3188 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3190 QVERIFY(input->hasActiveFocus());
3192 input->setWidth(input->implicitWidth());
3194 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3195 int cursorRectangleChanges = 0;
3197 // test the text is scrolled so the preedit is visible.
3198 sendPreeditText(preeditText.mid(0, 3), 1);
3199 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3200 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3201 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3203 // test the text is scrolled back when the preedit is removed.
3204 QInputMethodEvent imEvent;
3205 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3206 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3207 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3208 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3210 QTextLayout layout(preeditText);
3211 layout.setFont(input->font());
3212 if (!qmlDisableDistanceField()) {
3214 option.setUseDesignMetrics(true);
3215 layout.setTextOption(option);
3217 layout.beginLayout();
3218 QTextLine line = layout.createLine();
3221 // test if the preedit is larger than the text input that the
3222 // character preceding the cursor is still visible.
3223 qreal x = input->positionToRectangle(0).x();
3224 for (int i = 0; i < 3; ++i) {
3225 sendPreeditText(preeditText, i + 1);
3226 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3227 QVERIFY(input->cursorRectangle().right() >= width - 3);
3228 QVERIFY(input->positionToRectangle(0).x() < x);
3229 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3230 x = input->positionToRectangle(0).x();
3232 for (int i = 1; i >= 0; --i) {
3233 sendPreeditText(preeditText, i + 1);
3234 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3235 QVERIFY(input->cursorRectangle().right() >= width - 3);
3236 QVERIFY(input->positionToRectangle(0).x() > x);
3237 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3238 x = input->positionToRectangle(0).x();
3241 // Test incrementing the preedit cursor doesn't cause further
3242 // scrolling when right most text is visible.
3243 sendPreeditText(preeditText, preeditText.length() - 3);
3244 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3245 x = input->positionToRectangle(0).x();
3246 for (int i = 2; i >= 0; --i) {
3247 sendPreeditText(preeditText, preeditText.length() - i);
3248 QCOMPARE(input->positionToRectangle(0).x(), x);
3249 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3251 for (int i = 1; i < 3; ++i) {
3252 sendPreeditText(preeditText, preeditText.length() - i);
3253 QCOMPARE(input->positionToRectangle(0).x(), x);
3254 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3257 // Test disabling auto scroll.
3258 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3260 input->setAutoScroll(false);
3261 sendPreeditText(preeditText.mid(0, 3), 1);
3262 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3263 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3266 void tst_qquicktextinput::preeditCursorRectangle()
3268 QString preeditText = "super";
3270 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3272 view.requestActivateWindow();
3273 QTest::qWaitForWindowShown(&view);
3274 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3275 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3278 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3283 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3284 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3285 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3287 // Verify that the micro focus rect is positioned the same for position 0 as
3288 // it would be if there was no preedit text.
3289 sendPreeditText(preeditText, 0);
3290 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3291 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3292 QCOMPARE(currentRect, previousRect);
3293 QCOMPARE(input->cursorRectangle(), currentRect);
3294 QCOMPARE(cursor->pos(), currentRect.topLeft());
3296 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3297 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3299 // Verify that the micro focus rect moves to the left as the cursor position
3301 for (int i = 1; i <= 5; ++i) {
3302 sendPreeditText(preeditText, i);
3303 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3304 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3305 QVERIFY(previousRect.left() < currentRect.left());
3306 QCOMPARE(input->cursorRectangle(), currentRect);
3307 QCOMPARE(cursor->pos(), currentRect.topLeft());
3308 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3309 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3310 previousRect = currentRect;
3313 // Verify that if there is no preedit cursor then the micro focus rect is the
3314 // same as it would be if it were positioned at the end of the preedit text.
3315 sendPreeditText(preeditText, 0);
3316 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3317 QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3318 QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3319 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3320 QCOMPARE(currentRect, previousRect);
3321 QCOMPARE(input->cursorRectangle(), currentRect);
3322 QCOMPARE(cursor->pos(), currentRect.topLeft());
3323 QVERIFY(inputSpy.count() > 0);
3324 QVERIFY(panelSpy.count() > 0);
3327 void tst_qquicktextinput::inputContextMouseHandler()
3329 PlatformInputContext platformInputContext;
3330 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3331 inputMethodPrivate->testContext = &platformInputContext;
3333 QString text = "supercalifragisiticexpialidocious!";
3334 QQuickView view(testFileUrl("inputContext.qml"));
3335 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3338 input->setFocus(true);
3342 view.requestActivateWindow();
3343 QTest::qWaitForWindowShown(&view);
3344 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3346 QTextLayout layout(text);
3347 layout.setFont(input->font());
3348 if (!qmlDisableDistanceField()) {
3350 option.setUseDesignMetrics(true);
3351 layout.setTextOption(option);
3353 layout.beginLayout();
3354 QTextLine line = layout.createLine();
3357 const qreal x = line.cursorToX(2, QTextLine::Leading);
3358 const qreal y = line.height() / 2;
3359 QPoint position = QPointF(x, y).toPoint();
3361 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3362 QGuiApplication::sendEvent(input, &inputEvent);
3364 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3365 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3366 QGuiApplication::processEvents();
3368 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3369 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3370 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3373 void tst_qquicktextinput::inputMethodComposing()
3375 QString text = "supercalifragisiticexpialidocious!";
3377 QQuickView view(testFileUrl("inputContext.qml"));
3379 view.requestActivateWindow();
3380 QTest::qWaitForWindowShown(&view);
3381 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3382 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3384 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3386 QCOMPARE(input->isInputMethodComposing(), false);
3388 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3389 QGuiApplication::sendEvent(input, &event);
3391 QCOMPARE(input->isInputMethodComposing(), true);
3392 QCOMPARE(spy.count(), 1);
3395 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3396 QGuiApplication::sendEvent(input, &event);
3398 QCOMPARE(spy.count(), 1);
3401 QInputMethodEvent event;
3402 QGuiApplication::sendEvent(input, &event);
3404 QCOMPARE(input->isInputMethodComposing(), false);
3405 QCOMPARE(spy.count(), 2);
3407 // Changing the text while not composing doesn't alter the composing state.
3408 input->setText(text.mid(0, 16));
3409 QCOMPARE(input->isInputMethodComposing(), false);
3410 QCOMPARE(spy.count(), 2);
3413 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3414 QGuiApplication::sendEvent(input, &event);
3416 QCOMPARE(input->isInputMethodComposing(), true);
3417 QCOMPARE(spy.count(), 3);
3419 // Changing the text while composing cancels composition.
3420 input->setText(text.mid(0, 12));
3421 QCOMPARE(input->isInputMethodComposing(), false);
3422 QCOMPARE(spy.count(), 4);
3424 { // Preedit cursor positioned outside (empty) preedit; composing.
3425 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3426 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3427 QGuiApplication::sendEvent(input, &event);
3429 QCOMPARE(input->isInputMethodComposing(), true);
3430 QCOMPARE(spy.count(), 5);
3433 { // Cursor hidden; composing
3434 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3435 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3436 QGuiApplication::sendEvent(input, &event);
3438 QCOMPARE(input->isInputMethodComposing(), true);
3439 QCOMPARE(spy.count(), 5);
3441 { // Default cursor attributes; composing.
3442 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3443 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3444 QGuiApplication::sendEvent(input, &event);
3446 QCOMPARE(input->isInputMethodComposing(), true);
3447 QCOMPARE(spy.count(), 5);
3449 { // Selections are persisted: not composing
3450 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3451 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3452 QGuiApplication::sendEvent(input, &event);
3454 QCOMPARE(input->isInputMethodComposing(), false);
3455 QCOMPARE(spy.count(), 6);
3457 input->setCursorPosition(12);
3459 { // Formatting applied; composing.
3460 QTextCharFormat format;
3461 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3462 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3463 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3464 QGuiApplication::sendEvent(input, &event);
3466 QCOMPARE(input->isInputMethodComposing(), true);
3467 QCOMPARE(spy.count(), 7);
3470 QInputMethodEvent event;
3471 QGuiApplication::sendEvent(input, &event);
3473 QCOMPARE(input->isInputMethodComposing(), false);
3474 QCOMPARE(spy.count(), 8);
3477 void tst_qquicktextinput::inputMethodUpdate()
3479 PlatformInputContext platformInputContext;
3480 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3481 inputMethodPrivate->testContext = &platformInputContext;
3483 QQuickView view(testFileUrl("inputContext.qml"));
3485 view.requestActivateWindow();
3486 QTest::qWaitForWindowShown(&view);
3487 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3488 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3491 // text change even without cursor position change needs to trigger update
3492 input->setText("test");
3493 platformInputContext.clear();
3494 input->setText("xxxx");
3495 QVERIFY(platformInputContext.m_updateCallCount > 0);
3497 // input method event replacing text
3498 platformInputContext.clear();
3500 QInputMethodEvent inputMethodEvent;
3501 inputMethodEvent.setCommitString("y", -1, 1);
3502 QGuiApplication::sendEvent(input, &inputMethodEvent);
3504 QVERIFY(platformInputContext.m_updateCallCount > 0);
3506 // input method changing selection
3507 platformInputContext.clear();
3509 QList<QInputMethodEvent::Attribute> attributes;
3510 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3511 QInputMethodEvent inputMethodEvent("", attributes);
3512 QGuiApplication::sendEvent(input, &inputMethodEvent);
3514 QVERIFY(input->selectionStart() != input->selectionEnd());
3515 QVERIFY(platformInputContext.m_updateCallCount > 0);
3517 // programmatical selections trigger update
3518 platformInputContext.clear();
3520 QVERIFY(platformInputContext.m_updateCallCount > 0);
3523 platformInputContext.clear();
3524 QFont font = input->font();
3525 font.setBold(!font.bold());
3526 input->setFont(font);
3527 QVERIFY(platformInputContext.m_updateCallCount > 0);
3530 platformInputContext.clear();
3532 QInputMethodEvent inputMethodEvent;
3533 inputMethodEvent.setCommitString("y");
3534 QGuiApplication::sendEvent(input, &inputMethodEvent);
3536 QVERIFY(platformInputContext.m_updateCallCount > 0);
3538 // changing cursor position
3539 platformInputContext.clear();
3540 input->setCursorPosition(0);
3541 QVERIFY(platformInputContext.m_updateCallCount > 0);
3543 // read only disabled input method
3544 platformInputContext.clear();
3545 input->setReadOnly(true);
3546 QVERIFY(platformInputContext.m_updateCallCount > 0);
3547 input->setReadOnly(false);
3549 // no updates while no focus
3550 input->setFocus(false);
3551 platformInputContext.clear();
3552 input->setText("Foo");
3553 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3554 input->setCursorPosition(1);
3555 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3557 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3558 input->setReadOnly(true);
3559 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3562 void tst_qquicktextinput::cursorRectangleSize()
3564 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3565 QVERIFY(canvas->rootObject() != 0);
3566 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3568 // make sure cursor rectangle is not at (0,0)
3569 textInput->setX(10);
3570 textInput->setY(10);
3571 textInput->setCursorPosition(3);
3572 QVERIFY(textInput != 0);
3573 textInput->setFocus(true);
3575 canvas->requestActivateWindow();
3576 QTest::qWaitForWindowShown(canvas);
3577 QTRY_VERIFY(qApp->focusObject());
3579 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3580 qApp->sendEvent(qApp->focusObject(), &event);
3581 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3583 QRectF cursorRectFromItem = textInput->cursorRectangle();
3584 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3586 // item and input query cursor rectangles match
3587 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3589 // item cursor rectangle and positionToRectangle calculations match
3590 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3592 // item-canvas transform and input item transform match
3593 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3595 // input panel cursorRectangle property and tranformed item cursor rectangle match
3596 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3597 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3602 void tst_qquicktextinput::tripleClickSelectsAll()
3604 QString qmlfile = testFile("positionAt.qml");
3605 QQuickView view(QUrl::fromLocalFile(qmlfile));
3607 view.requestActivateWindow();
3608 QTest::qWaitForWindowShown(&view);
3610 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3612 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3615 QLatin1String hello("Hello world!");
3616 input->setSelectByMouse(true);
3617 input->setText(hello);
3619 // Clicking on the same point inside TextInput three times in a row
3620 // should trigger a triple click, thus selecting all the text.
3621 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3622 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3623 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3624 QGuiApplication::processEvents();
3625 QCOMPARE(input->selectedText(), hello);
3627 // Now it simulates user moving the mouse between the second and the third click.
3628 // In this situation, we don't expect a triple click.
3629 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3630 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3631 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3632 QGuiApplication::processEvents();
3633 QVERIFY(input->selectedText().isEmpty());
3635 // And now we press the third click too late, so no triple click event is triggered.
3636 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3637 QGuiApplication::processEvents();
3638 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3639 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3640 QGuiApplication::processEvents();
3641 QVERIFY(input->selectedText().isEmpty());
3644 void tst_qquicktextinput::QTBUG_19956_data()
3646 QTest::addColumn<QString>("url");
3647 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3648 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3652 void tst_qquicktextinput::getText_data()
3654 QTest::addColumn<QString>("text");
3655 QTest::addColumn<QString>("inputMask");
3656 QTest::addColumn<int>("start");
3657 QTest::addColumn<int>("end");
3658 QTest::addColumn<QString>("expectedText");
3660 QTest::newRow("all plain text")
3663 << 0 << standard.at(0).length()
3666 QTest::newRow("plain text sub string")
3670 << standard.at(0).mid(0, 12);
3672 QTest::newRow("plain text sub string reversed")
3676 << standard.at(0).mid(0, 12);
3678 QTest::newRow("plain text cropped beginning")
3682 << standard.at(0).mid(0, 4);
3684 QTest::newRow("plain text cropped end")
3687 << 23 << standard.at(0).length() + 8
3688 << standard.at(0).mid(23);
3690 QTest::newRow("plain text cropped beginning and end")
3693 << -9 << standard.at(0).length() + 4
3697 void tst_qquicktextinput::getText()
3699 QFETCH(QString, text);
3700 QFETCH(QString, inputMask);
3703 QFETCH(QString, expectedText);
3705 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3706 QQmlComponent textInputComponent(&engine);
3707 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3708 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3709 QVERIFY(textInput != 0);
3711 QCOMPARE(textInput->getText(start, end), expectedText);
3714 void tst_qquicktextinput::insert_data()
3716 QTest::addColumn<QString>("text");
3717 QTest::addColumn<QString>("inputMask");
3718 QTest::addColumn<int>("selectionStart");
3719 QTest::addColumn<int>("selectionEnd");
3720 QTest::addColumn<int>("insertPosition");
3721 QTest::addColumn<QString>("insertText");
3722 QTest::addColumn<QString>("expectedText");
3723 QTest::addColumn<int>("expectedSelectionStart");
3724 QTest::addColumn<int>("expectedSelectionEnd");
3725 QTest::addColumn<int>("expectedCursorPosition");
3726 QTest::addColumn<bool>("selectionChanged");
3727 QTest::addColumn<bool>("cursorPositionChanged");
3729 QTest::newRow("at cursor position (beginning)")
3734 << QString("Hello") + standard.at(0)
3738 QTest::newRow("at cursor position (end)")
3741 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3743 << standard.at(0) + QString("Hello")
3744 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3747 QTest::newRow("at cursor position (middle)")
3752 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3756 QTest::newRow("after cursor position (beginning)")
3761 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3765 QTest::newRow("before cursor position (end)")
3768 << standard.at(0).length() << standard.at(0).length() << 18
3770 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3771 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3774 QTest::newRow("before cursor position (middle)")
3779 << QString("Hello") + standard.at(0)
3783 QTest::newRow("after cursor position (middle)")
3786 << 18 << 18 << standard.at(0).length()
3788 << standard.at(0) + QString("Hello")
3792 QTest::newRow("before selection")
3797 << QString("Hello") + standard.at(0)
3801 QTest::newRow("before reversed selection")
3806 << QString("Hello") + standard.at(0)
3810 QTest::newRow("after selection")
3813 << 14 << 19 << standard.at(0).length()
3815 << standard.at(0) + QString("Hello")
3819 QTest::newRow("after reversed selection")
3822 << 19 << 14 << standard.at(0).length()
3824 << standard.at(0) + QString("Hello")
3828 QTest::newRow("into selection")
3833 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3837 QTest::newRow("into reversed selection")
3842 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3846 QTest::newRow("rich text into plain text")
3850 << QString("<b>Hello</b>")
3851 << QString("<b>Hello</b>") + standard.at(0)
3855 QTest::newRow("before start")
3864 QTest::newRow("past end")
3867 << 0 << 0 << standard.at(0).length() + 3
3873 const QString inputMask = "009.009.009.009";
3874 const QString ip = "192.168.2.14";
3876 QTest::newRow("mask: at cursor position (beginning)")
3881 << QString("125.168.2.14")
3885 QTest::newRow("mask: at cursor position (end)")
3888 << inputMask.length() << inputMask.length() << inputMask.length()
3891 << inputMask.length() << inputMask.length() << inputMask.length()
3894 QTest::newRow("mask: at cursor position (middle)")
3899 << QString("192.167.5.24")
3903 QTest::newRow("mask: after cursor position (beginning)")
3908 << QString("192.167.5.24")
3912 QTest::newRow("mask: before cursor position (end)")
3915 << inputMask.length() << inputMask.length() << 6
3917 << QString("192.167.5.24")
3918 << inputMask.length() << inputMask.length() << inputMask.length()
3921 QTest::newRow("mask: before cursor position (middle)")
3926 << QString("125.168.2.14")
3930 QTest::newRow("mask: after cursor position (middle)")
3939 QTest::newRow("mask: before selection")
3944 << QString("125.168.2.14")
3948 QTest::newRow("mask: before reversed selection")
3953 << QString("125.168.2.14")
3957 QTest::newRow("mask: after selection")
3966 QTest::newRow("mask: after reversed selection")
3975 QTest::newRow("mask: into selection")
3980 << QString("192.167.5.24")
3984 QTest::newRow("mask: into reversed selection")
3989 << QString("192.167.5.24")
3993 QTest::newRow("mask: before start")
4002 QTest::newRow("mask: past end")
4005 << 0 << 0 << ip.length() + 3
4011 QTest::newRow("mask: invalid characters")
4016 << QString("192.168.2.14")
4020 QTest::newRow("mask: mixed validity")
4024 << QString("a1b2c5")
4025 << QString("125.168.2.14")
4030 void tst_qquicktextinput::insert()
4032 QFETCH(QString, text);
4033 QFETCH(QString, inputMask);
4034 QFETCH(int, selectionStart);
4035 QFETCH(int, selectionEnd);
4036 QFETCH(int, insertPosition);
4037 QFETCH(QString, insertText);
4038 QFETCH(QString, expectedText);
4039 QFETCH(int, expectedSelectionStart);
4040 QFETCH(int, expectedSelectionEnd);
4041 QFETCH(int, expectedCursorPosition);
4042 QFETCH(bool, selectionChanged);
4043 QFETCH(bool, cursorPositionChanged);
4045 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4046 QQmlComponent textInputComponent(&engine);
4047 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4048 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4049 QVERIFY(textInput != 0);
4051 textInput->select(selectionStart, selectionEnd);
4053 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4054 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4055 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4056 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4057 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4059 textInput->insert(insertPosition, insertText);
4061 QCOMPARE(textInput->text(), expectedText);
4062 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4064 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4065 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4066 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4068 if (selectionStart > selectionEnd)
4069 qSwap(selectionStart, selectionEnd);
4071 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4072 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4073 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4074 QCOMPARE(textSpy.count() > 0, text != expectedText);
4075 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4078 void tst_qquicktextinput::remove_data()
4080 QTest::addColumn<QString>("text");
4081 QTest::addColumn<QString>("inputMask");
4082 QTest::addColumn<int>("selectionStart");
4083 QTest::addColumn<int>("selectionEnd");
4084 QTest::addColumn<int>("removeStart");
4085 QTest::addColumn<int>("removeEnd");
4086 QTest::addColumn<QString>("expectedText");
4087 QTest::addColumn<int>("expectedSelectionStart");
4088 QTest::addColumn<int>("expectedSelectionEnd");
4089 QTest::addColumn<int>("expectedCursorPosition");
4090 QTest::addColumn<bool>("selectionChanged");
4091 QTest::addColumn<bool>("cursorPositionChanged");
4093 QTest::newRow("from cursor position (beginning)")
4098 << standard.at(0).mid(5)
4102 QTest::newRow("to cursor position (beginning)")
4107 << standard.at(0).mid(5)
4111 QTest::newRow("to cursor position (end)")
4114 << standard.at(0).length() << standard.at(0).length()
4115 << standard.at(0).length() << standard.at(0).length() - 5
4116 << standard.at(0).mid(0, standard.at(0).length() - 5)
4117 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4120 QTest::newRow("to cursor position (end)")
4123 << standard.at(0).length() << standard.at(0).length()
4124 << standard.at(0).length() - 5 << standard.at(0).length()
4125 << standard.at(0).mid(0, standard.at(0).length() - 5)
4126 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4129 QTest::newRow("from cursor position (middle)")
4134 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4138 QTest::newRow("to cursor position (middle)")
4143 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4147 QTest::newRow("after cursor position (beginning)")
4152 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4156 QTest::newRow("before cursor position (end)")
4159 << standard.at(0).length() << standard.at(0).length()
4161 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4162 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4165 QTest::newRow("before cursor position (middle)")
4170 << standard.at(0).mid(5)
4174 QTest::newRow("after cursor position (middle)")
4179 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4183 QTest::newRow("before selection")
4188 << standard.at(0).mid(5)
4192 QTest::newRow("before reversed selection")
4197 << standard.at(0).mid(5)
4201 QTest::newRow("after selection")
4205 << standard.at(0).length() - 5 << standard.at(0).length()
4206 << standard.at(0).mid(0, standard.at(0).length() - 5)
4210 QTest::newRow("after reversed selection")
4214 << standard.at(0).length() - 5 << standard.at(0).length()
4215 << standard.at(0).mid(0, standard.at(0).length() - 5)
4219 QTest::newRow("from selection")
4224 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4228 QTest::newRow("from reversed selection")
4233 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4237 QTest::newRow("cropped beginning")
4242 << standard.at(0).mid(4)
4246 QTest::newRow("cropped end")
4250 << 23 << standard.at(0).length() + 8
4251 << standard.at(0).mid(0, 23)
4255 QTest::newRow("cropped beginning and end")
4259 << -9 << standard.at(0).length() + 4
4264 const QString inputMask = "009.009.009.009";
4265 const QString ip = "192.168.2.14";
4267 QTest::newRow("mask: from cursor position")
4272 << QString("192.16..14")
4276 QTest::newRow("mask: to cursor position")
4281 << QString("19.8.2.14")
4285 QTest::newRow("mask: before cursor position")
4290 << QString("2.168.2.14")
4294 QTest::newRow("mask: after cursor position")
4299 << QString("192.168.2.")
4303 QTest::newRow("mask: before selection")
4308 << QString("2.168.2.14")
4312 QTest::newRow("mask: before reversed selection")
4317 << QString("2.168.2.14")
4321 QTest::newRow("mask: after selection")
4326 << QString("192.168.2.")
4330 QTest::newRow("mask: after reversed selection")
4335 << QString("192.168.2.")
4339 QTest::newRow("mask: from selection")
4344 << QString("192.168..14")
4348 QTest::newRow("mask: from reversed selection")
4353 << QString("192.168..14")
4357 QTest::newRow("mask: cropped beginning")
4362 << QString(".168.2.14")
4366 QTest::newRow("mask: cropped end")
4371 << QString("192.168.2.1")
4375 QTest::newRow("mask: cropped beginning and end")
4385 void tst_qquicktextinput::remove()
4387 QFETCH(QString, text);
4388 QFETCH(QString, inputMask);
4389 QFETCH(int, selectionStart);
4390 QFETCH(int, selectionEnd);
4391 QFETCH(int, removeStart);
4392 QFETCH(int, removeEnd);
4393 QFETCH(QString, expectedText);
4394 QFETCH(int, expectedSelectionStart);
4395 QFETCH(int, expectedSelectionEnd);
4396 QFETCH(int, expectedCursorPosition);
4397 QFETCH(bool, selectionChanged);
4398 QFETCH(bool, cursorPositionChanged);
4400 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4401 QQmlComponent textInputComponent(&engine);
4402 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4403 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4404 QVERIFY(textInput != 0);
4406 textInput->select(selectionStart, selectionEnd);
4408 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4409 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4410 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4411 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4412 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4414 textInput->remove(removeStart, removeEnd);
4416 QCOMPARE(textInput->text(), expectedText);
4417 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4419 if (selectionStart > selectionEnd) //
4420 qSwap(selectionStart, selectionEnd);
4422 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4423 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4424 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4426 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4427 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4428 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4429 QCOMPARE(textSpy.count() > 0, text != expectedText);
4431 if (cursorPositionChanged) //
4432 QVERIFY(cursorPositionSpy.count() > 0);
4435 void tst_qquicktextinput::keySequence_data()
4437 QTest::addColumn<QString>("text");
4438 QTest::addColumn<QKeySequence>("sequence");
4439 QTest::addColumn<int>("selectionStart");
4440 QTest::addColumn<int>("selectionEnd");
4441 QTest::addColumn<int>("cursorPosition");
4442 QTest::addColumn<QString>("expectedText");
4443 QTest::addColumn<QString>("selectedText");
4444 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4445 QTest::addColumn<Qt::Key>("layoutDirection");
4447 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4449 QTest::newRow("select all")
4450 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4451 << 44 << standard.at(0) << standard.at(0)
4452 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4453 QTest::newRow("select start of line")
4454 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4455 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4456 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4457 QTest::newRow("select start of block")
4458 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4459 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4460 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4461 QTest::newRow("select end of line")
4462 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4463 << 44 << standard.at(0) << standard.at(0).mid(5)
4464 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4465 QTest::newRow("select end of document") // ### Not handled.
4466 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4467 << 3 << standard.at(0) << QString()
4468 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4469 QTest::newRow("select end of block")
4470 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4471 << 44 << standard.at(0) << standard.at(0).mid(18)
4472 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4473 QTest::newRow("delete end of line")
4474 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4475 << 24 << standard.at(0).mid(0, 24) << QString()
4476 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4477 QTest::newRow("move to start of line")
4478 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4479 << 0 << standard.at(0) << QString()
4480 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4481 QTest::newRow("move to start of block")
4482 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4483 << 0 << standard.at(0) << QString()
4484 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4485 QTest::newRow("move to next char")
4486 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4487 << 13 << standard.at(0) << QString()
4488 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4489 QTest::newRow("move to previous char (ltr)")
4490 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4491 << 2 << standard.at(0) << QString()
4492 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4493 QTest::newRow("move to previous char (rtl)")
4494 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4495 << 4 << standard.at(0) << QString()
4496 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4497 QTest::newRow("move to previous char with selection")
4498 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4499 << 3 << standard.at(0) << QString()
4500 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4501 QTest::newRow("select next char (ltr)")
4502 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4503 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4504 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4505 QTest::newRow("select next char (rtl)")
4506 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4507 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4508 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4509 QTest::newRow("select previous char (ltr)")
4510 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4511 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4512 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4513 QTest::newRow("select previous char (rtl)")
4514 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4515 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4516 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4517 QTest::newRow("move to next word (ltr)")
4518 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4519 << 10 << standard.at(0) << QString()
4520 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4521 QTest::newRow("move to next word (rtl)")
4522 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4523 << 4 << standard.at(0) << QString()
4524 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4525 QTest::newRow("move to next word (password,ltr)")
4526 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4527 << 44 << standard.at(0) << QString()
4528 << QQuickTextInput::Password << Qt::Key_Direction_L;
4529 QTest::newRow("move to next word (password,rtl)")
4530 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4531 << 0 << standard.at(0) << QString()
4532 << QQuickTextInput::Password << Qt::Key_Direction_R;
4533 QTest::newRow("move to previous word (ltr)")
4534 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4535 << 4 << standard.at(0) << QString()
4536 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4537 QTest::newRow("move to previous word (rlt)")
4538 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4539 << 10 << standard.at(0) << QString()
4540 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4541 QTest::newRow("move to previous word (password,ltr)")
4542 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4543 << 0 << standard.at(0) << QString()
4544 << QQuickTextInput::Password << Qt::Key_Direction_L;
4545 QTest::newRow("move to previous word (password,rtl)")
4546 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4547 << 44 << standard.at(0) << QString()
4548 << QQuickTextInput::Password << Qt::Key_Direction_R;
4549 QTest::newRow("select next word")
4550 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4551 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4552 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4553 QTest::newRow("select previous word")
4554 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4555 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4556 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4557 QTest::newRow("delete (selection)")
4558 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4559 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4560 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4561 QTest::newRow("delete (no selection)")
4562 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4563 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4564 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4565 QTest::newRow("delete end of word")
4566 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4567 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4568 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4569 QTest::newRow("delete start of word")
4570 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4571 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4572 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4575 void tst_qquicktextinput::keySequence()
4577 QFETCH(QString, text);
4578 QFETCH(QKeySequence, sequence);
4579 QFETCH(int, selectionStart);
4580 QFETCH(int, selectionEnd);
4581 QFETCH(int, cursorPosition);
4582 QFETCH(QString, expectedText);
4583 QFETCH(QString, selectedText);
4584 QFETCH(QQuickTextInput::EchoMode, echoMode);
4585 QFETCH(Qt::Key, layoutDirection);
4587 if (sequence.isEmpty()) {
4588 QSKIP("Key sequence is undefined");
4591 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4592 QQmlComponent textInputComponent(&engine);
4593 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4594 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4595 QVERIFY(textInput != 0);
4596 textInput->setEchoMode(echoMode);
4598 QQuickCanvas canvas;
4599 textInput->setParentItem(canvas.rootItem());
4601 canvas.requestActivateWindow();
4602 QTest::qWaitForWindowShown(&canvas);
4603 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4605 simulateKey(&canvas, layoutDirection);
4607 textInput->select(selectionStart, selectionEnd);
4609 simulateKeys(&canvas, sequence);
4611 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4612 QCOMPARE(textInput->text(), expectedText);
4613 QCOMPARE(textInput->selectedText(), selectedText);
4617 #define REPLACE_UNTIL_END 1
4619 void tst_qquicktextinput::undo_data()
4621 QTest::addColumn<QStringList>("insertString");
4622 QTest::addColumn<IntList>("insertIndex");
4623 QTest::addColumn<IntList>("insertMode");
4624 QTest::addColumn<QStringList>("expectedString");
4625 QTest::addColumn<bool>("use_keys");
4627 for (int i=0; i<2; i++) {
4628 QString keys_str = "keyboard";
4629 bool use_keys = true;
4631 keys_str = "insert";
4636 IntList insertIndex;
4638 QStringList insertString;
4639 QStringList expectedString;
4642 insertMode << NORMAL;
4643 insertString << "1";
4646 insertMode << NORMAL;
4647 insertString << "5";
4650 insertMode << NORMAL;
4651 insertString << "3";
4654 insertMode << NORMAL;
4655 insertString << "2";
4658 insertMode << NORMAL;
4659 insertString << "4";
4661 expectedString << "12345";
4662 expectedString << "1235";
4663 expectedString << "135";
4664 expectedString << "15";
4665 expectedString << "";
4667 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4675 IntList insertIndex;
4677 QStringList insertString;
4678 QStringList expectedString;
4681 insertMode << NORMAL;
4682 insertString << "World"; // World
4685 insertMode << NORMAL;
4686 insertString << "Hello"; // HelloWorld
4689 insertMode << NORMAL;
4690 insertString << "Well"; // WellHelloWorld
4693 insertMode << NORMAL;
4694 insertString << "There"; // WellHelloThereWorld;
4696 expectedString << "WellHelloThereWorld";
4697 expectedString << "WellHelloWorld";
4698 expectedString << "HelloWorld";
4699 expectedString << "World";
4700 expectedString << "";
4702 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4710 IntList insertIndex;
4712 QStringList insertString;
4713 QStringList expectedString;
4716 insertMode << NORMAL;
4717 insertString << "Ensuring";
4720 insertMode << NORMAL;
4721 insertString << " instan";
4724 insertMode << NORMAL;
4725 insertString << "an ";
4728 insertMode << REPLACE_UNTIL_END;
4729 insertString << " unique instance.";
4731 expectedString << "Ensuring a unique instance.";
4732 expectedString << "Ensuring an instan";
4733 expectedString << "Ensuring instan";
4734 expectedString << "";
4736 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4746 void tst_qquicktextinput::undo()
4748 QFETCH(QStringList, insertString);
4749 QFETCH(IntList, insertIndex);
4750 QFETCH(IntList, insertMode);
4751 QFETCH(QStringList, expectedString);
4753 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4754 QQmlComponent textInputComponent(&engine);
4755 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4756 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4757 QVERIFY(textInput != 0);
4759 QQuickCanvas canvas;
4760 textInput->setParentItem(canvas.rootItem());
4762 canvas.requestActivateWindow();
4763 QTest::qWaitForWindowShown(&canvas);
4764 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4766 QVERIFY(!textInput->canUndo());
4768 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4772 // STEP 1: First build up an undo history by inserting or typing some strings...
4773 for (i = 0; i < insertString.size(); ++i) {
4774 if (insertIndex[i] > -1)
4775 textInput->setCursorPosition(insertIndex[i]);
4777 // experimental stuff
4778 if (insertMode[i] == REPLACE_UNTIL_END) {
4779 textInput->select(insertIndex[i], insertIndex[i] + 8);
4781 // This is what I actually want...
4782 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4785 for (int j = 0; j < insertString.at(i).length(); j++)
4786 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4789 QCOMPARE(spy.count(), 1);
4791 // STEP 2: Next call undo several times and see if we can restore to the previous state
4792 for (i = 0; i < expectedString.size() - 1; ++i) {
4793 QCOMPARE(textInput->text(), expectedString[i]);
4794 QVERIFY(textInput->canUndo());
4798 // STEP 3: Verify that we have undone everything
4799 QVERIFY(textInput->text().isEmpty());
4800 QVERIFY(!textInput->canUndo());
4801 QCOMPARE(spy.count(), 2);
4804 void tst_qquicktextinput::redo_data()
4806 QTest::addColumn<QStringList>("insertString");
4807 QTest::addColumn<IntList>("insertIndex");
4808 QTest::addColumn<QStringList>("expectedString");
4811 IntList insertIndex;
4812 QStringList insertString;
4813 QStringList expectedString;
4816 insertString << "World"; // World
4818 insertString << "Hello"; // HelloWorld
4820 insertString << "Well"; // WellHelloWorld
4822 insertString << "There"; // WellHelloThereWorld;
4824 expectedString << "World";
4825 expectedString << "HelloWorld";
4826 expectedString << "WellHelloWorld";
4827 expectedString << "WellHelloThereWorld";
4829 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4833 void tst_qquicktextinput::redo()
4835 QFETCH(QStringList, insertString);
4836 QFETCH(IntList, insertIndex);
4837 QFETCH(QStringList, expectedString);
4839 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4840 QQmlComponent textInputComponent(&engine);
4841 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4842 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4843 QVERIFY(textInput != 0);
4845 QQuickCanvas canvas;
4846 textInput->setParentItem(canvas.rootItem());
4848 canvas.requestActivateWindow();
4849 QTest::qWaitForWindowShown(&canvas);
4850 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4852 QVERIFY(!textInput->canUndo());
4853 QVERIFY(!textInput->canRedo());
4855 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
4858 // inserts the diff strings at diff positions
4859 for (i = 0; i < insertString.size(); ++i) {
4860 if (insertIndex[i] > -1)
4861 textInput->setCursorPosition(insertIndex[i]);
4862 for (int j = 0; j < insertString.at(i).length(); j++)
4863 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4864 QVERIFY(textInput->canUndo());
4865 QVERIFY(!textInput->canRedo());
4868 QCOMPARE(spy.count(), 0);
4871 while (!textInput->text().isEmpty()) {
4872 QVERIFY(textInput->canUndo());
4874 QVERIFY(textInput->canRedo());
4877 QCOMPARE(spy.count(), 1);
4879 for (i = 0; i < expectedString.size(); ++i) {
4880 QVERIFY(textInput->canRedo());
4882 QCOMPARE(textInput->text() , expectedString[i]);
4883 QVERIFY(textInput->canUndo());
4885 QVERIFY(!textInput->canRedo());
4886 QCOMPARE(spy.count(), 2);
4889 void tst_qquicktextinput::undo_keypressevents_data()
4891 QTest::addColumn<KeyList>("keys");
4892 QTest::addColumn<QStringList>("expectedString");
4896 QStringList expectedString;
4899 << QKeySequence::MoveToStartOfLine
4906 << QKeySequence::MoveToEndOfLine
4909 expectedString << "BEVERYAFRAID!";
4910 expectedString << "BEVERYAFRAID";
4911 expectedString << "VERYAFRAID";
4912 expectedString << "AFRAID";
4914 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4917 QStringList expectedString;
4920 keys << "1234" << QKeySequence::MoveToStartOfLine
4922 << Qt::Key_Right << Qt::Key_Right
4924 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4928 expectedString << "12";
4929 expectedString << "1234";
4931 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4934 QStringList expectedString;
4938 << QKeySequence::MoveToStartOfLine
4940 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4942 << QKeySequence::Undo
4944 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4947 expectedString << "AB";
4948 expectedString << "AB12";
4950 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4953 QStringList expectedString;
4958 << Qt::Key_Left << Qt::Key_Left
4962 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4963 // overwriting '1234' with '5'
4965 // undoing deletion of 'AB'
4966 << QKeySequence::Undo
4967 // overwriting '1234' with '6'
4970 expectedString << "ab6cd";
4971 // for versions previous to 3.2 we overwrite needed two undo operations
4972 expectedString << "ab1234cd";
4973 expectedString << "abcd";
4975 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4978 QStringList expectedString;
4983 << Qt::Key_Backspace;
4985 expectedString << "AB";
4986 expectedString << "ABC";
4988 QTest::newRow("Inserts,backspace") << keys << expectedString;
4991 QStringList expectedString;
4995 << Qt::Key_Backspace
4999 expectedString << "ABZ";
5000 expectedString << "AB";
5001 expectedString << "ABC";
5003 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5006 QStringList expectedString;
5009 keys << "123" << QKeySequence::MoveToStartOfLine
5011 << QKeySequence::SelectEndOfLine
5012 // overwriting '123' with 'ABC'
5015 expectedString << "ABC";
5016 // for versions previous to 3.2 we overwrite needed two undo operations
5017 expectedString << "123";
5019 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5022 QStringList expectedString;
5026 << QKeySequence::Undo
5027 << QKeySequence::Redo;
5029 expectedString << "123";
5030 expectedString << QString();
5032 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5036 void tst_qquicktextinput::undo_keypressevents()
5038 QFETCH(KeyList, keys);
5039 QFETCH(QStringList, expectedString);
5041 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5042 QQmlComponent textInputComponent(&engine);
5043 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5044 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5045 QVERIFY(textInput != 0);
5047 QQuickCanvas canvas;
5048 textInput->setParentItem(canvas.rootItem());
5050 canvas.requestActivateWindow();
5051 QTest::qWaitForWindowShown(&canvas);
5052 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5054 simulateKeys(&canvas, keys);
5056 for (int i = 0; i < expectedString.size(); ++i) {
5057 QCOMPARE(textInput->text() , expectedString[i]);
5060 QVERIFY(textInput->text().isEmpty());
5063 void tst_qquicktextinput::QTBUG_19956()
5065 QFETCH(QString, url);
5067 QQuickView canvas(testFileUrl(url));
5069 canvas.requestActivateWindow();
5070 QTest::qWaitForWindowShown(&canvas);
5071 QVERIFY(canvas.rootObject() != 0);
5072 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5074 input->setFocus(true);
5075 QVERIFY(input->hasActiveFocus());
5077 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
5078 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5079 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
5080 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5082 canvas.rootObject()->setProperty("topvalue", 15);
5083 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
5084 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5086 canvas.rootObject()->setProperty("topvalue", 25);
5087 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
5088 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5090 canvas.rootObject()->setProperty("bottomvalue", 21);
5091 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
5092 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5094 canvas.rootObject()->setProperty("bottomvalue", 10);
5095 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5096 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5099 void tst_qquicktextinput::QTBUG_19956_regexp()
5101 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5103 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5104 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5106 QQuickView canvas(url);
5108 canvas.requestActivateWindow();
5109 QTest::qWaitForWindowShown(&canvas);
5110 QVERIFY(canvas.rootObject() != 0);
5111 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5113 input->setFocus(true);
5114 QVERIFY(input->hasActiveFocus());
5116 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5117 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5118 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
5119 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5121 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5122 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5123 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5125 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5126 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5127 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5130 void tst_qquicktextinput::implicitSize_data()
5132 QTest::addColumn<QString>("text");
5133 QTest::addColumn<QString>("wrap");
5134 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5135 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5138 void tst_qquicktextinput::implicitSize()
5140 QFETCH(QString, text);
5141 QFETCH(QString, wrap);
5142 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5143 QQmlComponent textComponent(&engine);
5144 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5145 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5147 QVERIFY(textObject->width() < textObject->implicitWidth());
5148 QVERIFY(textObject->height() == textObject->implicitHeight());
5150 textObject->resetWidth();
5151 QVERIFY(textObject->width() == textObject->implicitWidth());
5152 QVERIFY(textObject->height() == textObject->implicitHeight());
5155 void tst_qquicktextinput::implicitSizeBinding_data()
5157 implicitSize_data();
5160 void tst_qquicktextinput::implicitSizeBinding()
5162 QFETCH(QString, text);
5163 QFETCH(QString, wrap);
5164 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5165 QQmlComponent textComponent(&engine);
5166 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5167 QScopedPointer<QObject> object(textComponent.create());
5168 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5170 QCOMPARE(textObject->width(), textObject->implicitWidth());
5171 QCOMPARE(textObject->height(), textObject->implicitHeight());
5173 textObject->resetWidth();
5174 QCOMPARE(textObject->width(), textObject->implicitWidth());
5175 QCOMPARE(textObject->height(), textObject->implicitHeight());
5177 textObject->resetHeight();
5178 QCOMPARE(textObject->width(), textObject->implicitWidth());
5179 QCOMPARE(textObject->height(), textObject->implicitHeight());
5183 void tst_qquicktextinput::negativeDimensions()
5185 // Verify this doesn't assert during initialization.
5186 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5187 QScopedPointer<QObject> o(component.create());
5189 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5191 QCOMPARE(input->width(), qreal(-1));
5192 QCOMPARE(input->height(), qreal(-1));
5196 void tst_qquicktextinput::setInputMask_data()
5198 QTest::addColumn<QString>("mask");
5199 QTest::addColumn<QString>("input");
5200 QTest::addColumn<QString>("expectedText");
5201 QTest::addColumn<QString>("expectedDisplay");
5202 QTest::addColumn<bool>("insert_text");
5204 // both keyboard and insert()
5205 for (int i=0; i<2; i++) {
5206 bool insert_text = i==0 ? false : true;
5207 QString insert_mode = "keys ";
5209 insert_mode = "insert ";
5211 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5212 << QString("000.000.000.000")
5213 << QString("127.0.0.1")
5214 << QString("127.0.0.1")
5215 << QString("127.0 .0 .1 ")
5216 << bool(insert_text);
5217 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5218 << QString("HH:HH:HH:HH:HH:HH;#")
5219 << QString("00:E0:81:21:9E:8E")
5220 << QString("00:E0:81:21:9E:8E")
5221 << QString("00:E0:81:21:9E:8E")
5222 << bool(insert_text);
5223 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5224 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5225 << QString("AAe081219E8E")
5226 << QString("aa:E0:81:21:9E:8E")
5227 << QString("aa:E0:81:21:9E:8E")
5228 << bool(insert_text);
5229 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5230 << QString("BBBBBBBB;0")
5231 << QString("11011001")
5233 << QString("11011001")
5234 << bool(insert_text);
5235 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5236 << QString("bbbb.bbbb;-")
5237 << QString("110. 0001")
5238 << QString("110.0001")
5239 << QString("110-.0001")
5240 << bool(insert_text);
5241 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5242 << QString("000.000.000.000;0")
5243 << QString("127.0.0.1")
5244 << QString("127...1")
5245 << QString("127.000.000.100")
5246 << bool(insert_text);
5247 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5248 << QString("000.000.000.000")
5249 << QString(".0.0.1")
5250 << QString(".0.0.1")
5251 << QString(" .0 .0 .1 ")
5252 << bool(insert_text);
5253 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5254 << QString("000.000.000.000")
5257 << QString(" . . . ")
5258 << bool(insert_text);
5259 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5260 << QString("000.000.000.000;#")
5263 << QString("###.###.###.###")
5264 << bool(insert_text);
5265 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5266 << QString("000.000.000.000")
5267 << QString("1234123412341234")
5268 << QString("123.412.341.234")
5269 << QString("123.412.341.234")
5270 << bool(insert_text);
5271 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5276 << bool(insert_text);
5277 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5282 << bool(insert_text);
5284 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5289 << bool(insert_text);
5290 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5291 << QString("!A!A!A!A")
5295 << bool(insert_text);
5296 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5301 << bool(insert_text);
5303 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5304 << QString("{n}[0]")
5308 << bool(insert_text);
5309 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5310 << QString("\\\\N\\\\n00")
5314 << bool(insert_text);
5315 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5316 << QString("\\\\\\\\00")
5320 << bool(insert_text);
5321 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5322 << QString("\\\\(00\\\\)")
5326 << bool(insert_text);
5328 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5329 << QString(">AAAA<AAAA!AAAA")
5330 << QString("AbCdEfGhIjKl")
5331 << QString("ABCDefghIjKl")
5332 << QString("ABCDefghIjKl")
5333 << bool(insert_text);
5334 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5335 << QString(">aaaa<aaaa!aaaa")
5336 << QString("AbCdEfGhIjKl")
5337 << QString("ABCDefghIjKl")
5338 << QString("ABCDefghIjKl")
5339 << bool(insert_text);
5341 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5342 << QString(">A<A<A>A>A<A!A!A")
5343 << QString("AbCdEFGH")
5344 << QString("AbcDEfGH")
5345 << QString("AbcDEfGH")
5346 << bool(insert_text);
5347 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5348 << QString(">A<A<A>A>A<A!A!A")
5349 << QString("aBcDefgh")
5350 << QString("AbcDEfgh")
5351 << QString("AbcDEfgh")
5352 << bool(insert_text);
5353 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5354 << QString(">a<a<a>a>a<a!a!a")
5355 << QString("AbCdEFGH")
5356 << QString("AbcDEfGH")
5357 << QString("AbcDEfGH")
5358 << bool(insert_text);
5359 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5360 << QString(">a<a<a>a>a<a!a!a")
5361 << QString("aBcDefgh")
5362 << QString("AbcDEfgh")
5363 << QString("AbcDEfgh")
5364 << bool(insert_text);
5365 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5366 << QString(">H<H<H>H>H<H!H!H")
5367 << QString("aBcDef01")
5368 << QString("AbcDEf01")
5369 << QString("AbcDEf01")
5370 << bool(insert_text);
5371 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5372 << QString(">h<h<h>h>h<h!h!h")
5373 << QString("aBcDef92")
5374 << QString("AbcDEf92")
5375 << QString("AbcDEf92")
5376 << bool(insert_text);
5378 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5379 << QString("AAAAAAAA")
5380 << QString("A2#a;.0!")
5383 << bool(insert_text);
5384 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5386 << QString("f4f4f4f4")
5389 << bool(insert_text);
5390 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5391 << QString("9999;0")
5395 << bool(insert_text);
5399 void tst_qquicktextinput::setInputMask()
5401 QFETCH(QString, mask);
5402 QFETCH(QString, input);
5403 QFETCH(QString, expectedText);
5404 QFETCH(QString, expectedDisplay);
5405 QFETCH(bool, insert_text);
5407 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5408 QQmlComponent textInputComponent(&engine);
5409 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5410 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5411 QVERIFY(textInput != 0);
5413 // then either insert using insert() or keyboard
5415 textInput->insert(0, input);
5417 QQuickCanvas canvas;
5418 textInput->setParentItem(canvas.rootItem());
5420 canvas.requestActivateWindow();
5421 QTest::qWaitForWindowShown(&canvas);
5422 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5424 simulateKey(&canvas, Qt::Key_Home);
5425 for (int i = 0; i < input.length(); i++)
5426 QTest::keyClick(&canvas, input.at(i).toLatin1());
5429 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5430 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5432 QCOMPARE(textInput->text(), expectedText);
5433 QCOMPARE(textInput->displayText(), expectedDisplay);
5436 void tst_qquicktextinput::inputMask_data()
5438 QTest::addColumn<QString>("mask");
5439 QTest::addColumn<QString>("expectedMask");
5441 // if no mask is set a nul string should be returned
5442 QTest::newRow("nul 1") << QString("") << QString();
5443 QTest::newRow("nul 2") << QString() << QString();
5445 // try different masks
5446 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5447 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5448 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5449 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5451 // set an invalid input mask...
5452 // the current behaviour is that this exact (faulty) string is returned.
5453 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5455 // verify that we can unset the mask again
5456 QTest::newRow("unset") << QString("") << QString();
5459 void tst_qquicktextinput::inputMask()
5461 QFETCH(QString, mask);
5462 QFETCH(QString, expectedMask);
5464 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5465 QQmlComponent textInputComponent(&engine);
5466 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5467 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5468 QVERIFY(textInput != 0);
5470 QCOMPARE(textInput->inputMask(), expectedMask);
5473 void tst_qquicktextinput::clearInputMask()
5475 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
5476 QQmlComponent textInputComponent(&engine);
5477 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5478 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5479 QVERIFY(textInput != 0);
5481 QVERIFY(textInput->inputMask() != QString());
5482 textInput->setInputMask(QString());
5483 QCOMPARE(textInput->inputMask(), QString());
5486 void tst_qquicktextinput::keypress_inputMask_data()
5488 QTest::addColumn<QString>("mask");
5489 QTest::addColumn<KeyList>("keys");
5490 QTest::addColumn<QString>("expectedText");
5491 QTest::addColumn<QString>("expectedDisplayText");
5495 // inserting 'A1.2B'
5496 keys << Qt::Key_Home << "A1.2B";
5497 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
5502 keys << Qt::Key_Home << "0!P3";
5503 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
5508 keys << Qt::Key_Home
5510 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5514 // selecting all and delete
5515 keys << Qt::Key_Home
5516 << Key(Qt::ShiftModifier, Qt::Key_End)
5518 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5522 // inserting '12.12' then two backspaces
5523 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
5524 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
5529 keys << Qt::Key_Home << "12ab";
5530 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
5534 void tst_qquicktextinput::keypress_inputMask()
5536 QFETCH(QString, mask);
5537 QFETCH(KeyList, keys);
5538 QFETCH(QString, expectedText);
5539 QFETCH(QString, expectedDisplayText);
5541 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5542 QQmlComponent textInputComponent(&engine);
5543 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5544 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5545 QVERIFY(textInput != 0);
5547 QQuickCanvas canvas;
5548 textInput->setParentItem(canvas.rootItem());
5550 canvas.requestActivateWindow();
5551 QTest::qWaitForWindowShown(&canvas);
5552 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5554 simulateKeys(&canvas, keys);
5556 QCOMPARE(textInput->text(), expectedText);
5557 QCOMPARE(textInput->displayText(), expectedDisplayText);
5561 void tst_qquicktextinput::hasAcceptableInputMask_data()
5563 QTest::addColumn<QString>("optionalMask");
5564 QTest::addColumn<QString>("requiredMask");
5565 QTest::addColumn<QString>("invalid");
5566 QTest::addColumn<QString>("valid");
5568 QTest::newRow("Alphabetic optional and required")
5569 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
5570 QTest::newRow("Alphanumeric optional and require")
5571 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
5572 QTest::newRow("Any optional and required")
5573 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
5574 QTest::newRow("Numeric (0-9) required")
5575 << QString("0000") << QString("9999") << QString("11") << QString("1138");
5576 QTest::newRow("Numeric (1-9) optional and required")
5577 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
5580 void tst_qquicktextinput::hasAcceptableInputMask()
5582 QFETCH(QString, optionalMask);
5583 QFETCH(QString, requiredMask);
5584 QFETCH(QString, invalid);
5585 QFETCH(QString, valid);
5587 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + optionalMask + "\" }";
5588 QQmlComponent textInputComponent(&engine);
5589 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5590 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5591 QVERIFY(textInput != 0);
5593 QQuickCanvas canvas;
5594 textInput->setParentItem(canvas.rootItem());
5596 canvas.requestActivateWindow();
5597 QTest::qWaitForWindowShown(&canvas);
5598 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5600 // test that invalid input (for required) work for optionalMask
5601 textInput->setText(invalid);
5602 QVERIFY(textInput->hasAcceptableInput());
5604 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
5605 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
5607 // test requiredMask
5608 textInput->setInputMask(requiredMask);
5609 textInput->setText(invalid);
5610 QVERIFY(!textInput->hasAcceptableInput());
5612 textInput->setText(valid);
5613 QVERIFY(textInput->hasAcceptableInput());
5616 void tst_qquicktextinput::maskCharacter_data()
5618 QTest::addColumn<QString>("mask");
5619 QTest::addColumn<QString>("input");
5620 QTest::addColumn<bool>("expectedValid");
5622 QTest::newRow("Hex") << QString("H")
5623 << QString("0123456789abcdefABCDEF") << true;
5624 QTest::newRow("hex") << QString("h")
5625 << QString("0123456789abcdefABCDEF") << true;
5626 QTest::newRow("HexInvalid") << QString("H")
5627 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5629 QTest::newRow("hexInvalid") << QString("h")
5630 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5632 QTest::newRow("Bin") << QString("B")
5633 << QString("01") << true;
5634 QTest::newRow("bin") << QString("b")
5635 << QString("01") << true;
5636 QTest::newRow("BinInvalid") << QString("B")
5637 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5639 QTest::newRow("binInvalid") << QString("b")
5640 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5644 void tst_qquicktextinput::maskCharacter()
5646 QFETCH(QString, mask);
5647 QFETCH(QString, input);
5648 QFETCH(bool, expectedValid);
5650 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5651 QQmlComponent textInputComponent(&engine);
5652 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5653 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5654 QVERIFY(textInput != 0);
5656 QQuickCanvas canvas;
5657 textInput->setParentItem(canvas.rootItem());
5659 canvas.requestActivateWindow();
5660 QTest::qWaitForWindowShown(&canvas);
5661 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5663 for (int i = 0; i < input.size(); ++i) {
5664 QString in = QString(input.at(i));
5665 QString expected = expectedValid ? in : QString();
5666 textInput->setText(QString(input.at(i)));
5667 QCOMPARE(textInput->text(), expected);
5671 QTEST_MAIN(tst_qquicktextinput)
5673 #include "tst_qquicktextinput.moc"