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 "../../shared/testhttpserver.h"
45 #include <private/qinputmethod_p.h>
46 #include <QtQml/qqmlengine.h>
47 #include <QtQml/qqmlexpression.h>
49 #include <QtQuick/qquickview.h>
50 #include <QtGui/qguiapplication.h>
51 #include <QtGui/qstylehints.h>
52 #include <QInputMethod>
53 #include <private/qquicktextinput_p.h>
54 #include <private/qquicktextinput_p_p.h>
60 #include <Carbon/Carbon.h>
63 #include "qplatformdefs.h"
64 #include "../../shared/platforminputcontext.h"
66 #define SERVER_PORT 14460
67 #define SERVER_ADDR "http://localhost:14460"
69 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
70 Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
71 Q_DECLARE_METATYPE(Qt::Key)
73 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
75 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
77 // XXX This will be replaced by some clever persistent platform image store.
78 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
79 QString arch = "unknown-architecture"; // QTest needs to help with this.
81 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
83 if (!QFile::exists(expectfile)) {
84 actual.save(expectfile);
85 qWarning() << "created" << expectfile;
91 template <typename T> static T evaluate(QObject *scope, const QString &expression)
93 QQmlExpression expr(qmlContext(scope), scope, expression);
94 T result = expr.evaluate().value<T>();
96 qWarning() << expr.error().toString();
100 template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
102 typedef QPair<int, QChar> Key;
104 class tst_qquicktextinput : public QQmlDataTest
109 tst_qquicktextinput();
119 void persistentSelection();
120 void isRightToLeft_data();
121 void isRightToLeft();
122 void moveCursorSelection_data();
123 void moveCursorSelection();
124 void moveCursorSelectionSequence_data();
125 void moveCursorSelectionSequence();
126 void dragMouseSelection();
127 void mouseSelectionMode_data();
128 void mouseSelectionMode();
129 void mouseSelectionMode_accessors();
130 void selectByMouse();
132 void tripleClickSelectsAll();
134 void horizontalAlignment_data();
135 void horizontalAlignment();
136 void horizontalAlignment_RightToLeft();
137 void verticalAlignment();
149 void passwordCharacter();
150 void cursorDelegate_data();
151 void cursorDelegate();
152 void remoteCursorDelegate();
153 void cursorVisible();
154 void cursorRectangle_data();
155 void cursorRectangle();
157 void navigation_RTL();
159 void copyAndPasteKeySequence();
160 void canPasteEmpty();
162 void middleClickPaste();
166 void openInputPanel();
167 void setHAlignClearCache();
168 void focusOutClearSelection();
171 void passwordEchoDelay();
172 void geometrySignals();
175 void preeditAutoScroll();
176 void preeditCursorRectangle();
177 void inputContextMouseHandler();
178 void inputMethodComposing();
179 void inputMethodUpdate();
180 void cursorRectangleSize();
189 void keySequence_data();
196 void undo_keypressevents_data();
197 void undo_keypressevents();
199 void backspaceSurrogatePairs();
202 void QTBUG_19956_data();
203 void QTBUG_19956_regexp();
205 void implicitSize_data();
207 void implicitSizeBinding_data();
208 void implicitSizeBinding();
210 void negativeDimensions();
213 void setInputMask_data();
215 void inputMask_data();
217 void clearInputMask();
218 void keypress_inputMask_data();
219 void keypress_inputMask();
220 void hasAcceptableInputMask_data();
221 void hasAcceptableInputMask();
222 void maskCharacter_data();
223 void maskCharacter();
226 void simulateKey(QWindow *, int key);
228 void simulateKeys(QWindow *window, const QList<Key> &keys);
229 void simulateKeys(QWindow *window, const QKeySequence &sequence);
232 QStringList standard;
233 QStringList colorStrings;
236 typedef QList<int> IntList;
237 Q_DECLARE_METATYPE(IntList)
239 typedef QList<Key> KeyList;
240 Q_DECLARE_METATYPE(KeyList)
242 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
244 for (int i = 0; i < keys.count(); ++i) {
245 const int key = keys.at(i).first & ~Qt::KeyboardModifierMask;
246 const int modifiers = keys.at(i).first & Qt::KeyboardModifierMask;
247 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
249 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
250 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
252 QGuiApplication::sendEvent(window, &press);
253 QGuiApplication::sendEvent(window, &release);
257 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
259 for (int i = 0; i < sequence.count(); ++i) {
260 const int key = sequence[i];
261 const int modifiers = key & Qt::KeyboardModifierMask;
263 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
267 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
269 for (int i = 0; i < sequence.count(); ++i)
270 keys << Key(sequence[i], QChar());
274 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
276 for (int i = 0; i < N - 1; ++i) {
277 int key = QTest::asciiToKey(characters[i]);
278 QChar character = QLatin1Char(characters[i]);
279 keys << Key(key, character);
284 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
286 keys << Key(key, QChar());
290 void tst_qquicktextinput::cleanup()
292 // ensure not even skipped tests with custom input context leave it dangling
293 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
294 inputMethodPrivate->testContext = 0;
297 tst_qquicktextinput::tst_qquicktextinput()
299 standard << "the quick brown fox jumped over the lazy dog"
300 << "It's supercalifragisiticexpialidocious!"
305 colorStrings << "aliceblue"
319 void tst_qquicktextinput::text()
322 QQmlComponent textinputComponent(&engine);
323 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
324 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
326 QVERIFY(textinputObject != 0);
327 QCOMPARE(textinputObject->text(), QString(""));
328 QCOMPARE(textinputObject->length(), 0);
330 delete textinputObject;
333 for (int i = 0; i < standard.size(); i++)
335 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
336 QQmlComponent textinputComponent(&engine);
337 textinputComponent.setData(componentStr.toLatin1(), QUrl());
338 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
340 QVERIFY(textinputObject != 0);
341 QCOMPARE(textinputObject->text(), standard.at(i));
342 QCOMPARE(textinputObject->length(), standard.at(i).length());
344 delete textinputObject;
349 void tst_qquicktextinput::width()
351 // uses Font metrics to find the width for standard
353 QQmlComponent textinputComponent(&engine);
354 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
355 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
357 QVERIFY(textinputObject != 0);
358 QCOMPARE(textinputObject->width(), 0.0);
360 delete textinputObject;
363 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
365 for (int i = 0; i < standard.size(); i++)
367 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
368 QQmlComponent textinputComponent(&engine);
369 textinputComponent.setData(componentStr.toLatin1(), QUrl());
370 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
372 QString s = standard.at(i);
373 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
375 QTextLayout layout(s);
376 layout.setFont(textinputObject->font());
377 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
378 if (requiresUnhintedMetrics) {
380 option.setUseDesignMetrics(true);
381 layout.setTextOption(option);
384 layout.beginLayout();
386 QTextLine line = layout.createLine();
393 qreal metricWidth = ceil(layout.boundingRect().width());
395 QVERIFY(textinputObject != 0);
396 int delta = abs(int(int(textinputObject->width()) - metricWidth));
397 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
399 delete textinputObject;
403 void tst_qquicktextinput::font()
405 //test size, then bold, then italic, then family
407 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
408 QQmlComponent textinputComponent(&engine);
409 textinputComponent.setData(componentStr.toLatin1(), QUrl());
410 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
412 QVERIFY(textinputObject != 0);
413 QCOMPARE(textinputObject->font().pointSize(), 40);
414 QCOMPARE(textinputObject->font().bold(), false);
415 QCOMPARE(textinputObject->font().italic(), false);
417 delete textinputObject;
421 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
422 QQmlComponent textinputComponent(&engine);
423 textinputComponent.setData(componentStr.toLatin1(), QUrl());
424 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
426 QVERIFY(textinputObject != 0);
427 QCOMPARE(textinputObject->font().bold(), true);
428 QCOMPARE(textinputObject->font().italic(), false);
430 delete textinputObject;
434 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
435 QQmlComponent textinputComponent(&engine);
436 textinputComponent.setData(componentStr.toLatin1(), QUrl());
437 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
439 QVERIFY(textinputObject != 0);
440 QCOMPARE(textinputObject->font().italic(), true);
441 QCOMPARE(textinputObject->font().bold(), false);
443 delete textinputObject;
447 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
448 QQmlComponent textinputComponent(&engine);
449 textinputComponent.setData(componentStr.toLatin1(), QUrl());
450 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
452 QVERIFY(textinputObject != 0);
453 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
454 QCOMPARE(textinputObject->font().bold(), false);
455 QCOMPARE(textinputObject->font().italic(), false);
457 delete textinputObject;
461 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
462 QQmlComponent textinputComponent(&engine);
463 textinputComponent.setData(componentStr.toLatin1(), QUrl());
464 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
466 QVERIFY(textinputObject != 0);
467 QCOMPARE(textinputObject->font().family(), QString(""));
469 delete textinputObject;
473 void tst_qquicktextinput::color()
477 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello World\" }";
478 QQmlComponent texteditComponent(&engine);
479 texteditComponent.setData(componentStr.toLatin1(), QUrl());
480 QScopedPointer<QObject> object(texteditComponent.create());
481 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(object.data());
483 QVERIFY(textInputObject);
484 QCOMPARE(textInputObject->color(), QColor("black"));
485 QCOMPARE(textInputObject->selectionColor(), QColor::fromRgba(0xFF000080));
486 QCOMPARE(textInputObject->selectedTextColor(), QColor("white"));
488 QSignalSpy colorSpy(textInputObject, SIGNAL(colorChanged()));
489 QSignalSpy selectionColorSpy(textInputObject, SIGNAL(selectionColorChanged()));
490 QSignalSpy selectedTextColorSpy(textInputObject, SIGNAL(selectedTextColorChanged()));
492 textInputObject->setColor(QColor("white"));
493 QCOMPARE(textInputObject->color(), QColor("white"));
494 QCOMPARE(colorSpy.count(), 1);
496 textInputObject->setSelectionColor(QColor("black"));
497 QCOMPARE(textInputObject->selectionColor(), QColor("black"));
498 QCOMPARE(selectionColorSpy.count(), 1);
500 textInputObject->setSelectedTextColor(QColor("blue"));
501 QCOMPARE(textInputObject->selectedTextColor(), QColor("blue"));
502 QCOMPARE(selectedTextColorSpy.count(), 1);
504 textInputObject->setColor(QColor("white"));
505 QCOMPARE(colorSpy.count(), 1);
507 textInputObject->setSelectionColor(QColor("black"));
508 QCOMPARE(selectionColorSpy.count(), 1);
510 textInputObject->setSelectedTextColor(QColor("blue"));
511 QCOMPARE(selectedTextColorSpy.count(), 1);
513 textInputObject->setColor(QColor("black"));
514 QCOMPARE(textInputObject->color(), QColor("black"));
515 QCOMPARE(colorSpy.count(), 2);
517 textInputObject->setSelectionColor(QColor("blue"));
518 QCOMPARE(textInputObject->selectionColor(), QColor("blue"));
519 QCOMPARE(selectionColorSpy.count(), 2);
521 textInputObject->setSelectedTextColor(QColor("white"));
522 QCOMPARE(textInputObject->selectedTextColor(), QColor("white"));
523 QCOMPARE(selectedTextColorSpy.count(), 2);
527 for (int i = 0; i < colorStrings.size(); i++)
529 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
530 QQmlComponent textinputComponent(&engine);
531 textinputComponent.setData(componentStr.toLatin1(), QUrl());
532 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
533 QVERIFY(textinputObject != 0);
534 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
536 delete textinputObject;
539 //test selection color
540 for (int i = 0; i < colorStrings.size(); i++)
542 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
543 QQmlComponent textinputComponent(&engine);
544 textinputComponent.setData(componentStr.toLatin1(), QUrl());
545 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
546 QVERIFY(textinputObject != 0);
547 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
549 delete textinputObject;
552 //test selected text color
553 for (int i = 0; i < colorStrings.size(); i++)
555 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
556 QQmlComponent textinputComponent(&engine);
557 textinputComponent.setData(componentStr.toLatin1(), QUrl());
558 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
559 QVERIFY(textinputObject != 0);
560 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
562 delete textinputObject;
566 QString colorStr = "#AA001234";
567 QColor testColor("#001234");
568 testColor.setAlpha(170);
570 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
571 QQmlComponent textinputComponent(&engine);
572 textinputComponent.setData(componentStr.toLatin1(), QUrl());
573 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
575 QVERIFY(textinputObject != 0);
576 QCOMPARE(textinputObject->color(), testColor);
578 delete textinputObject;
582 void tst_qquicktextinput::wrap()
585 // for specified width and wrap set true
587 QQmlComponent textComponent(&engine);
588 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
589 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
590 textHeight = textObject->height();
592 QVERIFY(textObject != 0);
593 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
594 QCOMPARE(textObject->width(), 300.);
599 for (int i = 0; i < standard.count(); i++) {
600 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
601 QQmlComponent textComponent(&engine);
602 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
603 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
605 QVERIFY(textObject != 0);
606 QCOMPARE(textObject->width(), 30.);
607 QVERIFY(textObject->height() > textHeight);
609 int oldHeight = textObject->height();
610 textObject->setWidth(100);
611 QVERIFY(textObject->height() < oldHeight);
617 QQmlComponent component(&engine);
618 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
619 QScopedPointer<QObject> object(component.create());
620 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
623 QSignalSpy spy(input, SIGNAL(wrapModeChanged()));
625 QCOMPARE(input->wrapMode(), QQuickTextInput::NoWrap);
627 input->setWrapMode(QQuickTextInput::Wrap);
628 QCOMPARE(input->wrapMode(), QQuickTextInput::Wrap);
629 QCOMPARE(spy.count(), 1);
631 input->setWrapMode(QQuickTextInput::Wrap);
632 QCOMPARE(spy.count(), 1);
634 input->setWrapMode(QQuickTextInput::NoWrap);
635 QCOMPARE(input->wrapMode(), QQuickTextInput::NoWrap);
636 QCOMPARE(spy.count(), 2);
640 void tst_qquicktextinput::selection()
642 QString testStr = standard[0];
643 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
644 QQmlComponent textinputComponent(&engine);
645 textinputComponent.setData(componentStr.toLatin1(), QUrl());
646 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
647 QVERIFY(textinputObject != 0);
650 //Test selection follows cursor
651 for (int i=0; i<= testStr.size(); i++) {
652 textinputObject->setCursorPosition(i);
653 QCOMPARE(textinputObject->cursorPosition(), i);
654 QCOMPARE(textinputObject->selectionStart(), i);
655 QCOMPARE(textinputObject->selectionEnd(), i);
656 QVERIFY(textinputObject->selectedText().isNull());
659 textinputObject->setCursorPosition(0);
660 QVERIFY(textinputObject->cursorPosition() == 0);
661 QVERIFY(textinputObject->selectionStart() == 0);
662 QVERIFY(textinputObject->selectionEnd() == 0);
663 QVERIFY(textinputObject->selectedText().isNull());
665 // Verify invalid positions are ignored.
666 textinputObject->setCursorPosition(-1);
667 QVERIFY(textinputObject->cursorPosition() == 0);
668 QVERIFY(textinputObject->selectionStart() == 0);
669 QVERIFY(textinputObject->selectionEnd() == 0);
670 QVERIFY(textinputObject->selectedText().isNull());
672 textinputObject->setCursorPosition(textinputObject->text().count()+1);
673 QVERIFY(textinputObject->cursorPosition() == 0);
674 QVERIFY(textinputObject->selectionStart() == 0);
675 QVERIFY(textinputObject->selectionEnd() == 0);
676 QVERIFY(textinputObject->selectedText().isNull());
679 for (int i=0; i<= testStr.size(); i++) {
680 textinputObject->select(0,i);
681 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
683 for (int i=0; i<= testStr.size(); i++) {
684 textinputObject->select(i,testStr.size());
685 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
688 textinputObject->setCursorPosition(0);
689 QVERIFY(textinputObject->cursorPosition() == 0);
690 QVERIFY(textinputObject->selectionStart() == 0);
691 QVERIFY(textinputObject->selectionEnd() == 0);
692 QVERIFY(textinputObject->selectedText().isNull());
694 //Test Error Ignoring behaviour
695 textinputObject->setCursorPosition(0);
696 QVERIFY(textinputObject->selectedText().isNull());
697 textinputObject->select(-10,0);
698 QVERIFY(textinputObject->selectedText().isNull());
699 textinputObject->select(100,110);
700 QVERIFY(textinputObject->selectedText().isNull());
701 textinputObject->select(0,-10);
702 QVERIFY(textinputObject->selectedText().isNull());
703 textinputObject->select(0,100);
704 QVERIFY(textinputObject->selectedText().isNull());
705 textinputObject->select(0,10);
706 QVERIFY(textinputObject->selectedText().size() == 10);
707 textinputObject->select(-10,10);
708 QVERIFY(textinputObject->selectedText().size() == 10);
709 textinputObject->select(100,101);
710 QVERIFY(textinputObject->selectedText().size() == 10);
711 textinputObject->select(0,-10);
712 QVERIFY(textinputObject->selectedText().size() == 10);
713 textinputObject->select(0,100);
714 QVERIFY(textinputObject->selectedText().size() == 10);
716 textinputObject->deselect();
717 QVERIFY(textinputObject->selectedText().isNull());
718 textinputObject->select(0,10);
719 QVERIFY(textinputObject->selectedText().size() == 10);
720 textinputObject->deselect();
721 QVERIFY(textinputObject->selectedText().isNull());
723 // test input method selection
724 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
725 textinputObject->setFocus(true);
727 QList<QInputMethodEvent::Attribute> attributes;
728 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
729 QInputMethodEvent event("", attributes);
730 QGuiApplication::sendEvent(textinputObject, &event);
732 QCOMPARE(selectionSpy.count(), 1);
733 QCOMPARE(textinputObject->selectionStart(), 12);
734 QCOMPARE(textinputObject->selectionEnd(), 17);
736 delete textinputObject;
739 void tst_qquicktextinput::persistentSelection()
741 QQuickView window(testFileUrl("persistentSelection.qml"));
743 window.requestActivateWindow();
744 QTest::qWaitForWindowActive(&window);
746 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(window.rootObject());
748 QVERIFY(input->hasActiveFocus());
750 QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
752 QCOMPARE(input->persistentSelection(), false);
754 input->setPersistentSelection(false);
755 QCOMPARE(input->persistentSelection(), false);
756 QCOMPARE(spy.count(), 0);
759 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
761 input->setFocus(false);
762 QCOMPARE(input->property("selected").toString(), QString());
764 input->setFocus(true);
765 QCOMPARE(input->property("selected").toString(), QString());
767 input->setPersistentSelection(true);
768 QCOMPARE(input->persistentSelection(), true);
769 QCOMPARE(spy.count(), 1);
772 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
774 input->setFocus(false);
775 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
777 input->setFocus(true);
778 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
781 void tst_qquicktextinput::isRightToLeft_data()
783 QTest::addColumn<QString>("text");
784 QTest::addColumn<bool>("emptyString");
785 QTest::addColumn<bool>("firstCharacter");
786 QTest::addColumn<bool>("lastCharacter");
787 QTest::addColumn<bool>("middleCharacter");
788 QTest::addColumn<bool>("startString");
789 QTest::addColumn<bool>("midString");
790 QTest::addColumn<bool>("endString");
792 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
793 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
794 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
795 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
796 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
797 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;
798 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
801 void tst_qquicktextinput::isRightToLeft()
803 QFETCH(QString, text);
804 QFETCH(bool, emptyString);
805 QFETCH(bool, firstCharacter);
806 QFETCH(bool, lastCharacter);
807 QFETCH(bool, middleCharacter);
808 QFETCH(bool, startString);
809 QFETCH(bool, midString);
810 QFETCH(bool, endString);
812 QQuickTextInput textInput;
813 textInput.setText(text);
815 // first test that the right string is delivered to the QString::isRightToLeft()
816 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
817 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
818 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
819 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
820 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
821 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
823 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
824 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
826 // then test that the feature actually works
827 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
828 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
829 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
830 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
831 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
832 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
834 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
835 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
838 void tst_qquicktextinput::moveCursorSelection_data()
840 QTest::addColumn<QString>("testStr");
841 QTest::addColumn<int>("cursorPosition");
842 QTest::addColumn<int>("movePosition");
843 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
844 QTest::addColumn<int>("selectionStart");
845 QTest::addColumn<int>("selectionEnd");
846 QTest::addColumn<bool>("reversible");
848 // () contains the text selected by the cursor.
849 // <> contains the actual selection.
851 QTest::newRow("(t)he|characters")
852 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
853 QTest::newRow("do(g)|characters")
854 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
855 QTest::newRow("jum(p)ed|characters")
856 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
857 QTest::newRow("jumped( )over|characters")
858 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
859 QTest::newRow("(the )|characters")
860 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
861 QTest::newRow("( dog)|characters")
862 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
863 QTest::newRow("( jumped )|characters")
864 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
865 QTest::newRow("th(e qu)ick|characters")
866 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
867 QTest::newRow("la(zy d)og|characters")
868 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
869 QTest::newRow("jum(ped ov)er|characters")
870 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
871 QTest::newRow("()the|characters")
872 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
873 QTest::newRow("dog()|characters")
874 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
875 QTest::newRow("jum()ped|characters")
876 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
878 QTest::newRow("<(t)he>|words")
879 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
880 QTest::newRow("<do(g)>|words")
881 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
882 QTest::newRow("<jum(p)ed>|words")
883 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
884 QTest::newRow("<jumped( )>over|words,ltr")
885 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
886 QTest::newRow("jumped<( )over>|words,rtl")
887 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
888 QTest::newRow("<(the )>quick|words,ltr")
889 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
890 QTest::newRow("<(the )quick>|words,rtl")
891 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
892 QTest::newRow("<lazy( dog)>|words,ltr")
893 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
894 QTest::newRow("lazy<( dog)>|words,rtl")
895 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
896 QTest::newRow("<fox( jumped )>over|words,ltr")
897 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
898 QTest::newRow("fox<( jumped )over>|words,rtl")
899 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
900 QTest::newRow("<th(e qu)ick>|words")
901 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
902 QTest::newRow("<la(zy d)og|words>")
903 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
904 QTest::newRow("<jum(ped ov)er>|words")
905 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
906 QTest::newRow("<()>the|words")
907 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
908 QTest::newRow("dog<()>|words")
909 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
910 QTest::newRow("jum<()>ped|words")
911 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
913 QTest::newRow("Hello<(,)> |words")
914 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
915 QTest::newRow("Hello<(, )>world|words,ltr")
916 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
917 QTest::newRow("Hello<(, )world>|words,rtl")
918 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
919 QTest::newRow("<Hel(lo, )>world|words,ltr")
920 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
921 QTest::newRow("<Hel(lo, )world>|words,rtl")
922 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
923 QTest::newRow("<Hel(lo)>,|words")
924 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
925 QTest::newRow("Hello<()>,|words")
926 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
927 QTest::newRow("Hello,<()>|words")
928 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
929 QTest::newRow("Hello<,( )>world|words,ltr")
930 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
931 QTest::newRow("Hello,<( )world>|words,rtl")
932 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
933 QTest::newRow("Hello<,( world)>|words,ltr")
934 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
935 QTest::newRow("Hello,<( world)>|words,rtl")
936 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
937 QTest::newRow("Hello<,( world!)>|words,ltr")
938 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
939 QTest::newRow("Hello,<( world!)>|words,rtl")
940 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
941 QTest::newRow("Hello<(, world!)>|words")
942 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
943 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
945 // QTest::newRow("world<(!)>|words")
946 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
947 QTest::newRow("world!<()>)|words")
948 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
949 QTest::newRow("world<()>!)|words")
950 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
952 QTest::newRow("<(,)>olleH |words")
953 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
954 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
955 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
956 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
957 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
958 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
959 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
960 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
961 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
962 QTest::newRow(",<(ol)leH>,|words")
963 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
964 QTest::newRow(",<()>olleH|words")
965 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
966 QTest::newRow("<()>,olleH|words")
967 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
968 QTest::newRow("<dlrow( )>,olleH|words,ltr")
969 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
970 QTest::newRow("dlrow<( ),>olleH|words,rtl")
971 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
972 QTest::newRow("<(dlrow )>,olleH|words,ltr")
973 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
974 QTest::newRow("<(dlrow ),>olleH|words,rtl")
975 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
976 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
977 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
978 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
979 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
980 QTest::newRow("(!dlrow ,)olleH|words")
981 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
982 QTest::newRow("<(!)>dlrow|words")
983 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
984 QTest::newRow("<()>!dlrow|words")
985 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
986 QTest::newRow("!<()>dlrow|words")
987 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
989 QTest::newRow(" <s(pac)ey> text |words")
990 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
991 QTest::newRow(" spacey <t(ex)t> |words")
992 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
993 QTest::newRow("<( )>spacey text |words|ltr")
994 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
995 QTest::newRow("<( )spacey> text |words|rtl")
996 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
997 QTest::newRow("spacey <text( )>|words|ltr")
998 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
1000 // QTest::newRow("spacey text<( )>|words|rtl")
1001 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
1002 QTest::newRow("<()> spacey text |words")
1003 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
1004 QTest::newRow(" spacey text <()>|words")
1005 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
1008 void tst_qquicktextinput::moveCursorSelection()
1010 QFETCH(QString, testStr);
1011 QFETCH(int, cursorPosition);
1012 QFETCH(int, movePosition);
1013 QFETCH(QQuickTextInput::SelectionMode, mode);
1014 QFETCH(int, selectionStart);
1015 QFETCH(int, selectionEnd);
1016 QFETCH(bool, reversible);
1018 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1019 QQmlComponent textinputComponent(&engine);
1020 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1021 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1022 QVERIFY(textinputObject != 0);
1024 textinputObject->setCursorPosition(cursorPosition);
1025 textinputObject->moveCursorSelection(movePosition, mode);
1027 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1028 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1029 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1032 textinputObject->setCursorPosition(movePosition);
1033 textinputObject->moveCursorSelection(cursorPosition, mode);
1035 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1036 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1037 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1040 delete textinputObject;
1043 void tst_qquicktextinput::moveCursorSelectionSequence_data()
1045 QTest::addColumn<QString>("testStr");
1046 QTest::addColumn<int>("cursorPosition");
1047 QTest::addColumn<int>("movePosition1");
1048 QTest::addColumn<int>("movePosition2");
1049 QTest::addColumn<int>("selection1Start");
1050 QTest::addColumn<int>("selection1End");
1051 QTest::addColumn<int>("selection2Start");
1052 QTest::addColumn<int>("selection2End");
1054 // () contains the text selected by the cursor.
1055 // <> contains the actual selection.
1056 // ^ is the revised cursor position.
1057 // {} contains the revised selection.
1059 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1064 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1069 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1074 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1079 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1084 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1089 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1094 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1099 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1104 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1109 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1114 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1119 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1124 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1129 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1134 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1139 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1144 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1150 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1155 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1161 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1166 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1172 QTest::newRow("{<(^} sp)acey> text |ltr")
1177 QTest::newRow("{<( ^}sp)acey> text |ltr")
1182 QTest::newRow("<( {s^p)acey>} text |rtl")
1187 QTest::newRow("<( {^sp)acey>} text |rtl")
1193 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1198 QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1203 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1208 QTest::newRow(" spacey {<te(xt^} )>|ltr")
1215 void tst_qquicktextinput::moveCursorSelectionSequence()
1217 QFETCH(QString, testStr);
1218 QFETCH(int, cursorPosition);
1219 QFETCH(int, movePosition1);
1220 QFETCH(int, movePosition2);
1221 QFETCH(int, selection1Start);
1222 QFETCH(int, selection1End);
1223 QFETCH(int, selection2Start);
1224 QFETCH(int, selection2End);
1226 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1227 QQmlComponent textinputComponent(&engine);
1228 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1229 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1230 QVERIFY(textinputObject != 0);
1232 textinputObject->setCursorPosition(cursorPosition);
1234 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1235 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1236 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1237 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1239 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1240 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1241 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1242 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1244 delete textinputObject;
1247 void tst_qquicktextinput::dragMouseSelection()
1249 QString qmlfile = testFile("mouseselection_true.qml");
1251 QQuickView window(QUrl::fromLocalFile(qmlfile));
1254 window.requestActivateWindow();
1255 QTest::qWaitForWindowActive(&window);
1257 QVERIFY(window.rootObject() != 0);
1258 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1259 QVERIFY(textInputObject != 0);
1261 // press-and-drag-and-release from x1 to x2
1264 int y = textInputObject->height()/2;
1265 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1266 QTest::mouseMove(&window, QPoint(x2, y));
1267 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1270 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1271 QVERIFY(str1.length() > 3);
1273 // press and drag the current selection.
1276 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1277 QTest::mouseMove(&window, QPoint(x2, y));
1278 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1280 QString str2 = textInputObject->selectedText();
1281 QVERIFY(str2.length() > 3);
1283 QVERIFY(str1 != str2);
1286 void tst_qquicktextinput::mouseSelectionMode_data()
1288 QTest::addColumn<QString>("qmlfile");
1289 QTest::addColumn<bool>("selectWords");
1290 QTest::addColumn<bool>("focus");
1291 QTest::addColumn<bool>("focusOnPress");
1294 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1295 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1296 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1297 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1298 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1299 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1300 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1301 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1302 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1305 void tst_qquicktextinput::mouseSelectionMode()
1307 QFETCH(QString, qmlfile);
1308 QFETCH(bool, selectWords);
1309 QFETCH(bool, focus);
1310 QFETCH(bool, focusOnPress);
1312 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1314 QQuickView window(QUrl::fromLocalFile(qmlfile));
1317 window.requestActivateWindow();
1318 QTest::qWaitForWindowActive(&window);
1320 QVERIFY(window.rootObject() != 0);
1321 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1322 QVERIFY(textInputObject != 0);
1324 textInputObject->setFocus(focus);
1325 textInputObject->setFocusOnPress(focusOnPress);
1327 // press-and-drag-and-release from x1 to x2
1330 int y = textInputObject->height()/2;
1331 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1332 QTest::mouseMove(&window, QPoint(x2,y)); // doesn't work
1333 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1336 QTRY_COMPARE(textInputObject->selectedText(), text);
1338 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1339 QVERIFY(textInputObject->selectedText() != text);
1343 void tst_qquicktextinput::mouseSelectionMode_accessors()
1345 QQmlComponent component(&engine);
1346 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1347 QScopedPointer<QObject> object(component.create());
1348 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1351 QSignalSpy spy(input, SIGNAL(mouseSelectionModeChanged(SelectionMode)));
1353 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1355 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1356 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectWords);
1357 QCOMPARE(spy.count(), 1);
1359 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1360 QCOMPARE(spy.count(), 1);
1362 input->setMouseSelectionMode(QQuickTextInput::SelectCharacters);
1363 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1364 QCOMPARE(spy.count(), 2);
1367 void tst_qquicktextinput::selectByMouse()
1369 QQmlComponent component(&engine);
1370 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1371 QScopedPointer<QObject> object(component.create());
1372 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1375 QSignalSpy spy(input, SIGNAL(selectByMouseChanged(bool)));
1377 QCOMPARE(input->selectByMouse(), false);
1379 input->setSelectByMouse(true);
1380 QCOMPARE(input->selectByMouse(), true);
1381 QCOMPARE(spy.count(), 1);
1382 QCOMPARE(spy.at(0).at(0).toBool(), true);
1384 input->setSelectByMouse(true);
1385 QCOMPARE(spy.count(), 1);
1387 input->setSelectByMouse(false);
1388 QCOMPARE(input->selectByMouse(), false);
1389 QCOMPARE(spy.count(), 2);
1390 QCOMPARE(spy.at(1).at(0).toBool(), false);
1393 void tst_qquicktextinput::renderType()
1395 QQmlComponent component(&engine);
1396 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1397 QScopedPointer<QObject> object(component.create());
1398 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1401 QSignalSpy spy(input, SIGNAL(renderTypeChanged()));
1403 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1405 input->setRenderType(QQuickTextInput::NativeRendering);
1406 QCOMPARE(input->renderType(), QQuickTextInput::NativeRendering);
1407 QCOMPARE(spy.count(), 1);
1409 input->setRenderType(QQuickTextInput::NativeRendering);
1410 QCOMPARE(spy.count(), 1);
1412 input->setRenderType(QQuickTextInput::QtRendering);
1413 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1414 QCOMPARE(spy.count(), 2);
1417 void tst_qquicktextinput::horizontalAlignment_data()
1419 QTest::addColumn<int>("hAlign");
1420 QTest::addColumn<QString>("expectfile");
1422 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1423 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1424 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1427 void tst_qquicktextinput::horizontalAlignment()
1429 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1431 QFETCH(int, hAlign);
1432 QFETCH(QString, expectfile);
1434 QQuickView window(testFileUrl("horizontalAlignment.qml"));
1437 QVERIFY(QTest::qWaitForWindowExposed(&window));
1439 QObject *ob = window.rootObject();
1441 ob->setProperty("horizontalAlignment",hAlign);
1442 QImage actual = window.grabWindow();
1444 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1446 QImage expect(expectfile);
1448 QCOMPARE(actual,expect);
1451 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1453 PlatformInputContext platformInputContext;
1454 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1455 inputMethodPrivate->testContext = &platformInputContext;
1457 QQuickView window(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1458 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1459 QVERIFY(textInput != 0);
1462 const QString rtlText = textInput->text();
1464 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1465 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1467 // implicit alignment should follow the reading direction of RTL text
1468 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1469 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1470 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1471 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1473 // explicitly left aligned
1474 textInput->setHAlign(QQuickTextInput::AlignLeft);
1475 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1476 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1477 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1479 // explicitly right aligned
1480 textInput->setHAlign(QQuickTextInput::AlignRight);
1481 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1482 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1483 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1484 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1486 // explicitly center aligned
1487 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1488 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1489 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1490 QVERIFY(textInput->boundingRect().left() > 0);
1491 QVERIFY(textInput->boundingRect().right() < textInput->width());
1493 // reseted alignment should go back to following the text reading direction
1494 textInput->resetHAlign();
1495 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1496 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1497 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1498 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1500 // mirror the text item
1501 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1503 // mirrored implicit alignment should continue to follow the reading direction of the text
1504 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1505 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1506 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1507 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1509 // explicitly right aligned behaves as left aligned
1510 textInput->setHAlign(QQuickTextInput::AlignRight);
1511 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1512 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1513 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1515 // mirrored explicitly left aligned behaves as right aligned
1516 textInput->setHAlign(QQuickTextInput::AlignLeft);
1517 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1518 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1519 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1520 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1522 // disable mirroring
1523 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1524 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1525 textInput->resetHAlign();
1527 // English text should be implicitly left aligned
1528 textInput->setText("Hello world!");
1529 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1530 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1532 window.requestActivateWindow();
1533 QTest::qWaitForWindowActive(&window);
1534 QVERIFY(textInput->hasActiveFocus());
1536 // If there is no committed text, the preedit text should determine the alignment.
1537 textInput->setText(QString());
1538 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1539 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1540 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1541 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1543 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1544 // redundant as an actual input method may take care of it.
1545 { QInputMethodEvent ev; QGuiApplication::sendEvent(textInput, &ev); }
1547 // empty text with implicit alignment follows the system locale-based
1548 // keyboard input direction from QInputMethod::inputDirection()
1549 textInput->setText("");
1550 platformInputContext.setInputDirection(Qt::LeftToRight);
1551 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1552 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1553 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1555 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1556 platformInputContext.setInputDirection(Qt::RightToLeft);
1557 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1558 QCOMPARE(cursorRectangleSpy.count(), 1);
1559 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1560 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1561 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1563 // set input direction while having content
1564 platformInputContext.setInputDirection(Qt::LeftToRight);
1565 textInput->setText("a");
1566 platformInputContext.setInputDirection(Qt::RightToLeft);
1567 QTest::keyClick(&window, Qt::Key_Backspace);
1568 QVERIFY(textInput->text().isEmpty());
1569 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1570 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1571 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1573 // input direction changed while not having focus
1574 platformInputContext.setInputDirection(Qt::LeftToRight);
1575 textInput->setFocus(false);
1576 platformInputContext.setInputDirection(Qt::RightToLeft);
1577 textInput->setFocus(true);
1578 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1579 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1580 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1582 textInput->setHAlign(QQuickTextInput::AlignRight);
1583 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1584 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1585 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1587 // neutral text should fall back to input direction
1588 textInput->setFocus(true);
1589 textInput->resetHAlign();
1590 textInput->setText(" ()((=<>");
1591 platformInputContext.setInputDirection(Qt::LeftToRight);
1592 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1593 platformInputContext.setInputDirection(Qt::RightToLeft);
1594 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1596 // changing width keeps right aligned cursor on proper position
1597 textInput->setText("");
1598 textInput->setWidth(500);
1599 QVERIFY(textInput->positionToRectangle(0).x() > textInput->width() / 2);
1602 void tst_qquicktextinput::verticalAlignment()
1604 QQuickView window(testFileUrl("horizontalAlignment.qml"));
1605 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1606 QVERIFY(textInput != 0);
1609 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1610 QVERIFY(textInput->boundingRect().bottom() < window.height() / 2);
1611 QVERIFY(textInput->cursorRectangle().bottom() < window.height() / 2);
1612 QVERIFY(textInput->positionToRectangle(0).bottom() < window.height() / 2);
1615 textInput->setVAlign(QQuickTextInput::AlignBottom);
1616 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1617 QVERIFY(textInput->boundingRect().top() > window.height() / 2);
1618 QVERIFY(textInput->cursorRectangle().top() > window.height() / 2);
1619 QVERIFY(textInput->positionToRectangle(0).top() > window.height() / 2);
1621 // explicitly center aligned
1622 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1623 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1624 QVERIFY(textInput->boundingRect().top() < window.height() / 2);
1625 QVERIFY(textInput->boundingRect().bottom() > window.height() / 2);
1626 QVERIFY(textInput->cursorRectangle().top() < window.height() / 2);
1627 QVERIFY(textInput->cursorRectangle().bottom() > window.height() / 2);
1628 QVERIFY(textInput->positionToRectangle(0).top() < window.height() / 2);
1629 QVERIFY(textInput->positionToRectangle(0).bottom() > window.height() / 2);
1632 void tst_qquicktextinput::clipRect()
1634 QQmlComponent component(&engine);
1635 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1636 QScopedPointer<QObject> object(component.create());
1637 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1640 QCOMPARE(input->clipRect().x(), qreal(0));
1641 QCOMPARE(input->clipRect().y(), qreal(0));
1642 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1643 QCOMPARE(input->clipRect().height(), input->height());
1645 input->setText("Hello World");
1646 QCOMPARE(input->clipRect().x(), qreal(0));
1647 QCOMPARE(input->clipRect().y(), qreal(0));
1648 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1649 QCOMPARE(input->clipRect().height(), input->height());
1651 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1652 input->setWidth(input->width() / 2);
1653 QCOMPARE(input->clipRect().x(), qreal(0));
1654 QCOMPARE(input->clipRect().y(), qreal(0));
1655 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1656 QCOMPARE(input->clipRect().height(), input->height());
1658 input->setHeight(input->height() * 2);
1659 QCOMPARE(input->clipRect().x(), qreal(0));
1660 QCOMPARE(input->clipRect().y(), qreal(0));
1661 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1662 QCOMPARE(input->clipRect().height(), input->height());
1664 QQmlComponent cursorComponent(&engine);
1665 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1667 input->setCursorDelegate(&cursorComponent);
1668 input->setCursorVisible(true);
1670 // If a cursor delegate is used it's size should determine the excess width.
1671 QCOMPARE(input->clipRect().x(), qreal(0));
1672 QCOMPARE(input->clipRect().y(), qreal(0));
1673 QCOMPARE(input->clipRect().width(), input->width() + 8);
1674 QCOMPARE(input->clipRect().height(), input->height());
1676 // Alignment, auto scroll, wrapping all don't affect the clip rect.
1677 input->setAutoScroll(false);
1678 QCOMPARE(input->clipRect().x(), qreal(0));
1679 QCOMPARE(input->clipRect().y(), qreal(0));
1680 QCOMPARE(input->clipRect().width(), input->width() + 8);
1681 QCOMPARE(input->clipRect().height(), input->height());
1683 input->setHAlign(QQuickTextInput::AlignRight);
1684 QCOMPARE(input->clipRect().x(), qreal(0));
1685 QCOMPARE(input->clipRect().y(), qreal(0));
1686 QCOMPARE(input->clipRect().width(), input->width() + 8);
1687 QCOMPARE(input->clipRect().height(), input->height());
1689 input->setWrapMode(QQuickTextInput::Wrap);
1690 QCOMPARE(input->clipRect().x(), qreal(0));
1691 QCOMPARE(input->clipRect().y(), qreal(0));
1692 QCOMPARE(input->clipRect().width(), input->width() + 8);
1693 QCOMPARE(input->clipRect().height(), input->height());
1695 input->setVAlign(QQuickTextInput::AlignBottom);
1696 QCOMPARE(input->clipRect().x(), qreal(0));
1697 QCOMPARE(input->clipRect().y(), qreal(0));
1698 QCOMPARE(input->clipRect().width(), input->width() + 8);
1699 QCOMPARE(input->clipRect().height(), input->height());
1702 void tst_qquicktextinput::boundingRect()
1704 QQmlComponent component(&engine);
1705 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1706 QScopedPointer<QObject> object(component.create());
1707 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1711 layout.setFont(input->font());
1713 if (!qmlDisableDistanceField()) {
1715 option.setUseDesignMetrics(true);
1716 layout.setTextOption(option);
1718 layout.beginLayout();
1719 QTextLine line = layout.createLine();
1722 QCOMPARE(input->boundingRect().x(), qreal(0));
1723 QCOMPARE(input->boundingRect().y(), qreal(0));
1724 QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1725 QCOMPARE(input->boundingRect().height(), line.height());
1727 input->setText("Hello World");
1729 layout.setText(input->text());
1730 layout.beginLayout();
1731 line = layout.createLine();
1734 QCOMPARE(input->boundingRect().x(), qreal(0));
1735 QCOMPARE(input->boundingRect().y(), qreal(0));
1736 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1737 QCOMPARE(input->boundingRect().height(), line.height());
1739 // the size of the bounding rect shouldn't be bounded by the size of item.
1740 input->setWidth(input->width() / 2);
1741 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1742 QCOMPARE(input->boundingRect().y(), qreal(0));
1743 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1744 QCOMPARE(input->boundingRect().height(), line.height());
1746 input->setHeight(input->height() * 2);
1747 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1748 QCOMPARE(input->boundingRect().y(), qreal(0));
1749 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1750 QCOMPARE(input->boundingRect().height(), line.height());
1752 QQmlComponent cursorComponent(&engine);
1753 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1755 input->setCursorDelegate(&cursorComponent);
1756 input->setCursorVisible(true);
1758 // Don't include the size of a cursor delegate as it has its own bounding rect.
1759 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1760 QCOMPARE(input->boundingRect().y(), qreal(0));
1761 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1762 QCOMPARE(input->boundingRect().height(), line.height());
1764 // Bounding rect left aligned when auto scroll is disabled;
1765 input->setAutoScroll(false);
1766 QCOMPARE(input->boundingRect().x(), qreal(0));
1767 QCOMPARE(input->boundingRect().y(), qreal(0));
1768 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1769 QCOMPARE(input->boundingRect().height(), line.height());
1771 input->setHAlign(QQuickTextInput::AlignRight);
1772 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1773 QCOMPARE(input->boundingRect().y(), qreal(0));
1774 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1775 QCOMPARE(input->boundingRect().height(), line.height());
1777 input->setWrapMode(QQuickTextInput::Wrap);
1778 QCOMPARE(input->boundingRect().right(), input->width());
1779 QCOMPARE(input->boundingRect().y(), qreal(0));
1780 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1781 QVERIFY(input->boundingRect().height() > line.height());
1783 input->setVAlign(QQuickTextInput::AlignBottom);
1784 QCOMPARE(input->boundingRect().right(), input->width());
1785 QCOMPARE(input->boundingRect().bottom(), input->height());
1786 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1787 QVERIFY(input->boundingRect().height() > line.height());
1790 void tst_qquicktextinput::positionAt()
1792 QQuickView window(testFileUrl("positionAt.qml"));
1793 QVERIFY(window.rootObject() != 0);
1795 window.requestActivateWindow();
1796 QTest::qWaitForWindowActive(&window);
1798 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1799 QVERIFY(textinputObject != 0);
1801 // Check autoscrolled...
1803 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1805 QTextLayout layout(textinputObject->text());
1806 layout.setFont(textinputObject->font());
1808 if (!qmlDisableDistanceField()) {
1810 option.setUseDesignMetrics(true);
1811 layout.setTextOption(option);
1813 layout.beginLayout();
1814 QTextLine line = layout.createLine();
1817 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1818 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1819 int textWidth = floor(line.horizontalAdvance());
1821 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1822 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1824 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1825 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1826 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1828 // Check without autoscroll...
1829 textinputObject->setAutoScroll(false);
1830 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1832 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1833 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1835 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1836 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1838 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1839 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1840 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1842 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1843 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1845 QString preeditText = textinputObject->text().mid(0, pos);
1846 textinputObject->setText(textinputObject->text().mid(pos));
1847 textinputObject->setCursorPosition(0);
1849 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1850 QVERIFY(qGuiApp->focusObject());
1851 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1853 // Check all points within the preedit text return the same position.
1854 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1855 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1856 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1858 // Verify positioning returns to normal after the preedit text.
1859 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1860 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1862 { QInputMethodEvent inputEvent;
1863 QVERIFY(qGuiApp->focusObject());
1864 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1867 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1869 const qreal y0 = line.height() / 2;
1870 const qreal y1 = line.height() * 3 / 2;
1872 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1873 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1875 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1876 QVERIFY(newLinePos > pos);
1877 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1880 void tst_qquicktextinput::maxLength()
1882 QQuickView window(testFileUrl("maxLength.qml"));
1883 QVERIFY(window.rootObject() != 0);
1885 window.requestActivateWindow();
1886 QTest::qWaitForWindowActive(&window);
1888 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1889 QVERIFY(textinputObject != 0);
1890 QVERIFY(textinputObject->text().isEmpty());
1891 QVERIFY(textinputObject->maxLength() == 10);
1892 foreach (const QString &str, standard) {
1893 QVERIFY(textinputObject->text().length() <= 10);
1894 textinputObject->setText(str);
1895 QVERIFY(textinputObject->text().length() <= 10);
1898 textinputObject->setText("");
1899 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1900 for (int i=0; i<20; i++) {
1901 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1902 //simulateKey(&window, Qt::Key_A);
1903 QTest::keyPress(&window, Qt::Key_A);
1904 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1909 void tst_qquicktextinput::masks()
1911 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1912 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1913 QQuickView window(testFileUrl("masks.qml"));
1915 window.requestActivateWindow();
1916 QVERIFY(window.rootObject() != 0);
1917 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1918 QVERIFY(textinputObject != 0);
1919 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1920 QVERIFY(textinputObject->text().length() == 0);
1921 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1922 QCOMPARE(textinputObject->length(), 8);
1923 for (int i=0; i<10; i++) {
1924 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1925 QCOMPARE(textinputObject->length(), 8);
1926 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1927 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1928 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1929 //simulateKey(&window, Qt::Key_A);
1930 QTest::keyPress(&window, Qt::Key_A);
1931 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1936 void tst_qquicktextinput::validators()
1938 // Note that this test assumes that the validators are working properly
1939 // so you may need to run their tests first. All validators are checked
1940 // here to ensure that their exposure to QML is working.
1942 QLocale::setDefault(QLocale(QStringLiteral("C")));
1944 QQuickView window(testFileUrl("validators.qml"));
1946 window.requestActivateWindow();
1947 QTest::qWaitForWindowActive(&window);
1949 QVERIFY(window.rootObject() != 0);
1951 QLocale defaultLocale;
1952 QLocale enLocale("en");
1953 QLocale deLocale("de_DE");
1955 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("intInput")));
1957 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1959 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1960 QVERIFY(intValidator);
1961 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1962 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1963 intValidator->setLocaleName(enLocale.name());
1964 QCOMPARE(intValidator->localeName(), enLocale.name());
1965 QCOMPARE(intInput->validator()->locale(), enLocale);
1966 intValidator->resetLocaleName();
1967 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1968 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1970 intInput->setFocus(true);
1971 QTRY_VERIFY(intInput->hasActiveFocus());
1972 QCOMPARE(intInput->hasAcceptableInput(), false);
1973 QCOMPARE(intInput->property("acceptable").toBool(), false);
1974 QTest::keyPress(&window, Qt::Key_1);
1975 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
1977 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1978 QCOMPARE(intInput->hasAcceptableInput(), false);
1979 QCOMPARE(intInput->property("acceptable").toBool(), false);
1980 QCOMPARE(intSpy.count(), 0);
1981 QTest::keyPress(&window, Qt::Key_2);
1982 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
1984 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1985 QCOMPARE(intInput->hasAcceptableInput(), false);
1986 QCOMPARE(intInput->property("acceptable").toBool(), false);
1987 QCOMPARE(intSpy.count(), 0);
1988 QTest::keyPress(&window, Qt::Key_Period);
1989 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
1991 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1992 QCOMPARE(intInput->hasAcceptableInput(), false);
1993 QTest::keyPress(&window, Qt::Key_Comma);
1994 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
1996 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1997 QCOMPARE(intInput->hasAcceptableInput(), false);
1998 QTest::keyPress(&window, Qt::Key_Backspace);
1999 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2001 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
2002 QCOMPARE(intInput->hasAcceptableInput(), false);
2003 intValidator->setLocaleName(deLocale.name());
2004 QTest::keyPress(&window, Qt::Key_Period);
2005 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
2007 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
2008 QCOMPARE(intInput->hasAcceptableInput(), false);
2009 QTest::keyPress(&window, Qt::Key_Backspace);
2010 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2012 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
2013 QCOMPARE(intInput->hasAcceptableInput(), false);
2014 intValidator->resetLocaleName();
2015 QTest::keyPress(&window, Qt::Key_1);
2016 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2018 QCOMPARE(intInput->text(), QLatin1String("11"));
2019 QCOMPARE(intInput->hasAcceptableInput(), true);
2020 QCOMPARE(intInput->property("acceptable").toBool(), true);
2021 QCOMPARE(intSpy.count(), 1);
2022 QTest::keyPress(&window, Qt::Key_0);
2023 QTest::keyRelease(&window, Qt::Key_0, Qt::NoModifier ,10);
2025 QCOMPARE(intInput->text(), QLatin1String("11"));
2026 QCOMPARE(intInput->hasAcceptableInput(), true);
2027 QCOMPARE(intInput->property("acceptable").toBool(), true);
2028 QCOMPARE(intSpy.count(), 1);
2030 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("dblInput")));
2032 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
2034 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
2035 QVERIFY(dblValidator);
2036 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2037 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2038 dblValidator->setLocaleName(enLocale.name());
2039 QCOMPARE(dblValidator->localeName(), enLocale.name());
2040 QCOMPARE(dblInput->validator()->locale(), enLocale);
2041 dblValidator->resetLocaleName();
2042 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2043 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2045 dblInput->setFocus(true);
2046 QVERIFY(dblInput->hasActiveFocus() == true);
2047 QCOMPARE(dblInput->hasAcceptableInput(), false);
2048 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2049 QTest::keyPress(&window, Qt::Key_1);
2050 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2052 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2053 QCOMPARE(dblInput->hasAcceptableInput(), false);
2054 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2055 QCOMPARE(dblSpy.count(), 0);
2056 QTest::keyPress(&window, Qt::Key_2);
2057 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
2059 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2060 QCOMPARE(dblInput->hasAcceptableInput(), true);
2061 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2062 QCOMPARE(dblSpy.count(), 1);
2063 QTest::keyPress(&window, Qt::Key_Comma);
2064 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
2066 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2067 QCOMPARE(dblInput->hasAcceptableInput(), true);
2068 QTest::keyPress(&window, Qt::Key_1);
2069 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2071 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2072 QCOMPARE(dblInput->hasAcceptableInput(), true);
2073 dblValidator->setLocaleName(deLocale.name());
2074 QCOMPARE(dblInput->hasAcceptableInput(), true);
2075 QTest::keyPress(&window, Qt::Key_1);
2076 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2078 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2079 QCOMPARE(dblInput->hasAcceptableInput(), true);
2080 QTest::keyPress(&window, Qt::Key_1);
2081 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2083 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
2084 QCOMPARE(dblInput->hasAcceptableInput(), true);
2085 QTest::keyPress(&window, Qt::Key_Backspace);
2086 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2088 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2089 QCOMPARE(dblInput->hasAcceptableInput(), true);
2090 QTest::keyPress(&window, Qt::Key_Backspace);
2091 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2093 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2094 QCOMPARE(dblInput->hasAcceptableInput(), true);
2095 QTest::keyPress(&window, Qt::Key_Backspace);
2096 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2098 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2099 QCOMPARE(dblInput->hasAcceptableInput(), true);
2100 dblValidator->resetLocaleName();
2101 QTest::keyPress(&window, Qt::Key_Period);
2102 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
2104 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2105 QCOMPARE(dblInput->hasAcceptableInput(), true);
2106 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2107 QCOMPARE(dblSpy.count(), 1);
2108 QTest::keyPress(&window, Qt::Key_1);
2109 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2111 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2112 QCOMPARE(dblInput->hasAcceptableInput(), true);
2113 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2114 QCOMPARE(dblSpy.count(), 1);
2115 QTest::keyPress(&window, Qt::Key_1);
2116 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2118 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2119 QCOMPARE(dblInput->hasAcceptableInput(), true);
2120 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2121 QCOMPARE(dblSpy.count(), 1);
2122 QTest::keyPress(&window, Qt::Key_1);
2123 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2125 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2126 QCOMPARE(dblInput->hasAcceptableInput(), true);
2127 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2128 QCOMPARE(dblSpy.count(), 1);
2130 // Ensure the validator doesn't prevent characters being removed.
2131 dblInput->setValidator(intInput->validator());
2132 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
2133 QCOMPARE(dblInput->hasAcceptableInput(), false);
2134 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2135 QCOMPARE(dblSpy.count(), 2);
2136 QTest::keyPress(&window, Qt::Key_Backspace);
2137 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2139 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2140 QCOMPARE(dblInput->hasAcceptableInput(), false);
2141 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2142 QCOMPARE(dblSpy.count(), 2);
2143 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
2144 QTest::keyPress(&window, Qt::Key_1);
2145 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2147 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2148 QCOMPARE(dblInput->hasAcceptableInput(), false);
2149 QCOMPARE(dblSpy.count(), 2);
2150 QTest::keyPress(&window, Qt::Key_Backspace);
2151 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2153 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2154 QCOMPARE(dblInput->hasAcceptableInput(), false);
2155 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2156 QCOMPARE(dblSpy.count(), 2);
2157 QTest::keyPress(&window, Qt::Key_Backspace);
2158 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2160 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2161 QCOMPARE(dblInput->hasAcceptableInput(), false);
2162 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2163 QCOMPARE(dblSpy.count(), 2);
2164 QTest::keyPress(&window, Qt::Key_Backspace);
2165 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2167 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2168 QCOMPARE(dblInput->hasAcceptableInput(), false);
2169 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2170 QCOMPARE(dblSpy.count(), 2);
2171 QTest::keyPress(&window, Qt::Key_Backspace);
2172 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2174 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2175 QCOMPARE(dblInput->hasAcceptableInput(), false);
2176 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2177 QCOMPARE(dblSpy.count(), 2);
2178 QTest::keyPress(&window, Qt::Key_1);
2179 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2181 QCOMPARE(dblInput->text(), QLatin1String("11"));
2182 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2183 QCOMPARE(dblInput->hasAcceptableInput(), true);
2184 QCOMPARE(dblSpy.count(), 3);
2186 // Changing the validator properties will re-evaluate whether the input is acceptable.
2187 intValidator->setTop(10);
2188 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2189 QCOMPARE(dblInput->hasAcceptableInput(), false);
2190 QCOMPARE(dblSpy.count(), 4);
2191 intValidator->setTop(12);
2192 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2193 QCOMPARE(dblInput->hasAcceptableInput(), true);
2194 QCOMPARE(dblSpy.count(), 5);
2196 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("strInput")));
2198 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2199 strInput->setFocus(true);
2200 QVERIFY(strInput->hasActiveFocus() == true);
2201 QCOMPARE(strInput->hasAcceptableInput(), false);
2202 QCOMPARE(strInput->property("acceptable").toBool(), false);
2203 QTest::keyPress(&window, Qt::Key_1);
2204 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2206 QTRY_COMPARE(strInput->text(), QLatin1String(""));
2207 QCOMPARE(strInput->hasAcceptableInput(), false);
2208 QCOMPARE(strInput->property("acceptable").toBool(), false);
2209 QCOMPARE(strSpy.count(), 0);
2210 QTest::keyPress(&window, Qt::Key_A);
2211 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2213 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2214 QCOMPARE(strInput->hasAcceptableInput(), false);
2215 QCOMPARE(strInput->property("acceptable").toBool(), false);
2216 QCOMPARE(strSpy.count(), 0);
2217 QTest::keyPress(&window, Qt::Key_A);
2218 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2220 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2221 QCOMPARE(strInput->hasAcceptableInput(), true);
2222 QCOMPARE(strInput->property("acceptable").toBool(), true);
2223 QCOMPARE(strSpy.count(), 1);
2224 QTest::keyPress(&window, Qt::Key_A);
2225 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2227 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2228 QCOMPARE(strInput->hasAcceptableInput(), true);
2229 QCOMPARE(strInput->property("acceptable").toBool(), true);
2230 QCOMPARE(strSpy.count(), 1);
2231 QTest::keyPress(&window, Qt::Key_A);
2232 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2234 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2235 QCOMPARE(strInput->hasAcceptableInput(), true);
2236 QCOMPARE(strInput->property("acceptable").toBool(), true);
2237 QCOMPARE(strSpy.count(), 1);
2238 QTest::keyPress(&window, Qt::Key_A);
2239 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2241 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2242 QCOMPARE(strInput->hasAcceptableInput(), true);
2243 QCOMPARE(strInput->property("acceptable").toBool(), true);
2244 QCOMPARE(strSpy.count(), 1);
2246 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("unvalidatedInput")));
2247 QVERIFY(unvalidatedInput);
2248 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2249 unvalidatedInput->setFocus(true);
2250 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2251 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2252 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2253 QTest::keyPress(&window, Qt::Key_1);
2254 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2256 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2257 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2258 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2259 QCOMPARE(unvalidatedSpy.count(), 0);
2260 QTest::keyPress(&window, Qt::Key_A);
2261 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2263 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2264 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2265 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2266 QCOMPARE(unvalidatedSpy.count(), 0);
2269 void tst_qquicktextinput::inputMethods()
2271 QQuickView window(testFileUrl("inputmethods.qml"));
2273 window.requestActivateWindow();
2274 QTest::qWaitForWindowActive(&window);
2276 // test input method hints
2277 QVERIFY(window.rootObject() != 0);
2278 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(window.rootObject());
2279 QVERIFY(input != 0);
2280 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2281 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2282 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2283 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2284 QCOMPARE(inputMethodHintSpy.count(), 1);
2285 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2286 QCOMPARE(inputMethodHintSpy.count(), 1);
2289 QQuickTextInput plainInput;
2290 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2292 input->setFocus(true);
2293 QVERIFY(input->hasActiveFocus() == true);
2294 // test that input method event is committed
2295 QInputMethodEvent event;
2296 event.setCommitString( "My ", -12, 0);
2297 QTRY_COMPARE(qGuiApp->focusObject(), input);
2298 QGuiApplication::sendEvent(input, &event);
2299 QCOMPARE(input->text(), QString("My Hello world!"));
2301 input->setCursorPosition(2);
2302 event.setCommitString("Your", -2, 2);
2303 QGuiApplication::sendEvent(input, &event);
2304 QCOMPARE(input->text(), QString("Your Hello world!"));
2305 QCOMPARE(input->cursorPosition(), 4);
2307 input->setCursorPosition(7);
2308 event.setCommitString("Goodbye", -2, 5);
2309 QGuiApplication::sendEvent(input, &event);
2310 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2311 QCOMPARE(input->cursorPosition(), 12);
2313 input->setCursorPosition(8);
2314 event.setCommitString("Our", -8, 4);
2315 QGuiApplication::sendEvent(input, &event);
2316 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2317 QCOMPARE(input->cursorPosition(), 7);
2319 // input should reset selection even if replacement parameters are out of bounds
2320 input->setText("text");
2321 input->setCursorPosition(0);
2322 input->moveCursorSelection(input->text().length());
2323 event.setCommitString("replacement", -input->text().length(), input->text().length());
2324 QGuiApplication::sendEvent(input, &event);
2325 QCOMPARE(input->selectionStart(), input->selectionEnd());
2327 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2328 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2329 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2331 input->setReadOnly(true);
2332 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2333 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2337 TextInput element should only handle left/right keys until the cursor reaches
2338 the extent of the text, then they should ignore the keys.
2341 void tst_qquicktextinput::navigation()
2343 QQuickView window(testFileUrl("navigation.qml"));
2345 window.requestActivateWindow();
2347 QVERIFY(window.rootObject() != 0);
2349 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2351 QVERIFY(input != 0);
2352 input->setCursorPosition(0);
2353 QTRY_VERIFY(input->hasActiveFocus() == true);
2354 simulateKey(&window, Qt::Key_Left);
2355 QVERIFY(input->hasActiveFocus() == false);
2356 simulateKey(&window, Qt::Key_Right);
2357 QVERIFY(input->hasActiveFocus() == true);
2358 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2359 input->setCursorPosition(input->text().length());
2360 input->select(0,input->text().length());
2361 QVERIFY(input->selectionStart() != input->selectionEnd());
2362 simulateKey(&window, Qt::Key_Right);
2363 QVERIFY(input->selectionStart() == input->selectionEnd());
2364 QVERIFY(input->selectionStart() == input->text().length());
2365 QVERIFY(input->hasActiveFocus() == true);
2366 simulateKey(&window, Qt::Key_Right);
2367 QVERIFY(input->hasActiveFocus() == false);
2368 simulateKey(&window, Qt::Key_Left);
2369 QVERIFY(input->hasActiveFocus() == true);
2371 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2372 input->setCursorPosition(2);
2373 QCOMPARE(input->cursorPosition(),2);
2374 simulateKey(&window, Qt::Key_Up);
2375 QCOMPARE(input->cursorPosition(),2);
2376 simulateKey(&window, Qt::Key_Down);
2377 QCOMPARE(input->cursorPosition(),2);
2379 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2380 input->setText(QString());
2381 QCOMPARE(input->cursorPosition(), 0);
2382 simulateKey(&window, Qt::Key_Right);
2383 QCOMPARE(input->hasActiveFocus(), false);
2384 simulateKey(&window, Qt::Key_Left);
2385 QCOMPARE(input->hasActiveFocus(), true);
2386 simulateKey(&window, Qt::Key_Left);
2387 QCOMPARE(input->hasActiveFocus(), false);
2390 void tst_qquicktextinput::navigation_RTL()
2392 QQuickView window(testFileUrl("navigation.qml"));
2394 window.requestActivateWindow();
2396 QVERIFY(window.rootObject() != 0);
2398 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2400 QVERIFY(input != 0);
2401 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2402 input->setText(QString::fromUtf16(arabic_str, 11));
2404 input->setCursorPosition(0);
2405 QTRY_VERIFY(input->hasActiveFocus() == true);
2408 simulateKey(&window, Qt::Key_Right);
2409 QVERIFY(input->hasActiveFocus() == false);
2412 simulateKey(&window, Qt::Key_Left);
2413 QVERIFY(input->hasActiveFocus() == true);
2415 input->setCursorPosition(input->text().length());
2416 QVERIFY(input->hasActiveFocus() == true);
2419 simulateKey(&window, Qt::Key_Left);
2420 QVERIFY(input->hasActiveFocus() == false);
2423 simulateKey(&window, Qt::Key_Right);
2424 QVERIFY(input->hasActiveFocus() == true);
2427 void tst_qquicktextinput::copyAndPaste() {
2428 #ifndef QT_NO_CLIPBOARD
2432 PasteboardRef pasteboard;
2433 OSStatus status = PasteboardCreate(0, &pasteboard);
2434 if (status == noErr)
2435 CFRelease(pasteboard);
2437 QSKIP("This machine doesn't support the clipboard");
2441 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2442 QQmlComponent textInputComponent(&engine);
2443 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2444 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2445 QVERIFY(textInput != 0);
2448 QCOMPARE(textInput->text().length(), 12);
2449 textInput->select(0, textInput->text().length());
2451 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2452 QCOMPARE(textInput->selectedText().length(), 12);
2453 textInput->setCursorPosition(0);
2454 QVERIFY(textInput->canPaste());
2456 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2457 QCOMPARE(textInput->text().length(), 24);
2460 QVERIFY(textInput->canPaste());
2461 textInput->setReadOnly(true);
2462 QVERIFY(!textInput->canPaste());
2464 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2465 QCOMPARE(textInput->text().length(), 24);
2466 textInput->setReadOnly(false);
2467 QVERIFY(textInput->canPaste());
2469 // cut: no selection
2471 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2474 textInput->setCursorPosition(0);
2475 textInput->selectWord();
2476 QCOMPARE(textInput->selectedText(), QString("Hello"));
2479 textInput->setReadOnly(true);
2481 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2482 textInput->setReadOnly(false);
2484 // select all and cut
2485 textInput->selectAll();
2487 QCOMPARE(textInput->text().length(), 0);
2489 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2490 QCOMPARE(textInput->text().length(), 24);
2493 textInput->setCursorPosition(0);
2494 textInput->selectWord();
2496 // copy: no selection, previous copy retained;
2497 textInput->setCursorPosition(0);
2498 QCOMPARE(textInput->selectedText(), QString());
2500 textInput->setText(QString());
2502 QCOMPARE(textInput->text(), QString("Hello"));
2504 // clear copy buffer
2505 QClipboard *clipboard = QGuiApplication::clipboard();
2508 QVERIFY(!textInput->canPaste());
2510 // test that copy functionality is disabled
2511 // when echo mode is set to hide text/password mode
2514 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2515 textInput->setEchoMode(echoMode);
2516 textInput->setText("My password");
2517 textInput->select(0, textInput->text().length());
2519 if (echoMode == QQuickTextInput::Normal) {
2520 QVERIFY(!clipboard->text().isEmpty());
2521 QCOMPARE(clipboard->text(), QString("My password"));
2524 QVERIFY(clipboard->text().isEmpty());
2533 void tst_qquicktextinput::copyAndPasteKeySequence() {
2534 #ifndef QT_NO_CLIPBOARD
2538 PasteboardRef pasteboard;
2539 OSStatus status = PasteboardCreate(0, &pasteboard);
2540 if (status == noErr)
2541 CFRelease(pasteboard);
2543 QSKIP("This machine doesn't support the clipboard");
2547 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2548 QQmlComponent textInputComponent(&engine);
2549 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2550 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2551 QVERIFY(textInput != 0);
2553 QQuickWindow window;
2554 textInput->setParentItem(window.rootItem());
2556 window.requestActivateWindow();
2557 QTest::qWaitForWindowActive(&window);
2560 QVERIFY(textInput->hasActiveFocus());
2561 QCOMPARE(textInput->text().length(), 12);
2562 textInput->select(0, textInput->text().length());
2563 simulateKeys(&window, QKeySequence::Copy);
2564 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2565 QCOMPARE(textInput->selectedText().length(), 12);
2566 textInput->setCursorPosition(0);
2567 QVERIFY(textInput->canPaste());
2568 simulateKeys(&window, QKeySequence::Paste);
2569 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2570 QCOMPARE(textInput->text().length(), 24);
2572 // select all and cut
2573 simulateKeys(&window, QKeySequence::SelectAll);
2574 simulateKeys(&window, QKeySequence::Cut);
2575 QCOMPARE(textInput->text().length(), 0);
2576 simulateKeys(&window, QKeySequence::Paste);
2577 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2578 QCOMPARE(textInput->text().length(), 24);
2580 // clear copy buffer
2581 QClipboard *clipboard = QGuiApplication::clipboard();
2584 QVERIFY(!textInput->canPaste());
2586 // test that copy functionality is disabled
2587 // when echo mode is set to hide text/password mode
2590 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2591 textInput->setEchoMode(echoMode);
2592 textInput->setText("My password");
2593 textInput->select(0, textInput->text().length());
2594 simulateKeys(&window, QKeySequence::Copy);
2595 if (echoMode == QQuickTextInput::Normal) {
2596 QVERIFY(!clipboard->text().isEmpty());
2597 QCOMPARE(clipboard->text(), QString("My password"));
2600 QVERIFY(clipboard->text().isEmpty());
2609 void tst_qquicktextinput::canPasteEmpty() {
2610 #ifndef QT_NO_CLIPBOARD
2612 QGuiApplication::clipboard()->clear();
2614 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2615 QQmlComponent textInputComponent(&engine);
2616 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2617 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2618 QVERIFY(textInput != 0);
2620 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2621 QCOMPARE(textInput->canPaste(), cp);
2626 void tst_qquicktextinput::canPaste() {
2627 #ifndef QT_NO_CLIPBOARD
2629 QGuiApplication::clipboard()->setText("Some text");
2631 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2632 QQmlComponent textInputComponent(&engine);
2633 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2634 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2635 QVERIFY(textInput != 0);
2637 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2638 QCOMPARE(textInput->canPaste(), cp);
2643 void tst_qquicktextinput::middleClickPaste()
2645 #ifndef QT_NO_CLIPBOARD
2649 PasteboardRef pasteboard;
2650 OSStatus status = PasteboardCreate(0, &pasteboard);
2651 if (status == noErr)
2652 CFRelease(pasteboard);
2654 QSKIP("This machine doesn't support the clipboard");
2658 QQuickView window(testFileUrl("mouseselection_true.qml"));
2661 window.requestActivateWindow();
2662 QTest::qWaitForWindowActive(&window);
2664 QVERIFY(window.rootObject() != 0);
2665 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
2666 QVERIFY(textInputObject != 0);
2668 textInputObject->setFocus(true);
2670 QString originalText = textInputObject->text();
2671 QString selectedText = "234567";
2673 // press-and-drag-and-release from x1 to x2
2674 const QPoint p1 = textInputObject->positionToRectangle(2).center().toPoint();
2675 const QPoint p2 = textInputObject->positionToRectangle(8).center().toPoint();
2676 const QPoint p3 = textInputObject->positionToRectangle(1).center().toPoint();
2677 QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
2678 QTest::mouseMove(&window, p2);
2679 QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2);
2680 QTRY_COMPARE(textInputObject->selectedText(), selectedText);
2682 // Middle click pastes the selected text, assuming the platform supports it.
2683 QTest::mouseClick(&window, Qt::MiddleButton, Qt::NoModifier, p3);
2685 // ### This is to prevent double click detection from carrying over to the next test.
2686 QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 10);
2688 if (QGuiApplication::clipboard()->supportsSelection())
2689 QCOMPARE(textInputObject->text().mid(1, selectedText.length()), selectedText);
2691 QCOMPARE(textInputObject->text(), originalText);
2695 void tst_qquicktextinput::passwordCharacter()
2697 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2698 QQmlComponent textInputComponent(&engine);
2699 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2700 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2701 QVERIFY(textInput != 0);
2703 textInput->setPasswordCharacter("X");
2704 qreal implicitWidth = textInput->implicitWidth();
2705 textInput->setPasswordCharacter(".");
2707 // QTBUG-12383 content is updated and redrawn
2708 QVERIFY(textInput->implicitWidth() < implicitWidth);
2713 void tst_qquicktextinput::cursorDelegate_data()
2715 QTest::addColumn<QUrl>("source");
2716 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2717 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2718 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2721 void tst_qquicktextinput::cursorDelegate()
2723 QFETCH(QUrl, source);
2724 QQuickView view(source);
2726 view.requestActivateWindow();
2727 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2728 QVERIFY(textInputObject != 0);
2729 // Delegate is created on demand, and so won't be available immediately. Focus in or
2730 // setCursorVisible(true) will trigger creation.
2731 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2732 QVERIFY(!textInputObject->isCursorVisible());
2733 //Test Delegate gets created
2734 textInputObject->setFocus(true);
2735 QVERIFY(textInputObject->isCursorVisible());
2736 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2737 QVERIFY(delegateObject);
2738 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2739 //Test Delegate gets moved
2740 for (int i=0; i<= textInputObject->text().length(); i++) {
2741 textInputObject->setCursorPosition(i);
2742 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2743 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2745 textInputObject->setCursorPosition(0);
2746 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2747 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2749 // Test delegate gets moved on mouse press.
2750 textInputObject->setSelectByMouse(true);
2751 textInputObject->setCursorPosition(0);
2752 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2753 QTest::qWait(400); //ensure this isn't treated as a double-click
2754 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2756 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2757 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2758 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2760 // Test delegate gets moved on mouse drag
2761 textInputObject->setCursorPosition(0);
2762 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2763 QTest::qWait(400); //ensure this isn't treated as a double-click
2764 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2765 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2766 QGuiApplication::sendEvent(&view, &mv);
2767 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2769 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2770 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2772 textInputObject->setReadOnly(true);
2773 textInputObject->setCursorPosition(0);
2774 QTest::qWait(400); //ensure this isn't treated as a double-click
2775 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2777 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2778 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2779 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2781 textInputObject->setCursorPosition(0);
2782 QTest::qWait(400); //ensure this isn't treated as a double-click
2783 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2785 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2786 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2787 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2789 textInputObject->setCursorPosition(0);
2790 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2791 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2793 textInputObject->setReadOnly(false);
2795 // Delegate moved when text is entered
2796 textInputObject->setText(QString());
2797 for (int i = 0; i < 20; ++i) {
2798 QTest::keyClick(&view, Qt::Key_A);
2799 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2800 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2803 // Delegate moved when text is entered by im.
2804 textInputObject->setText(QString());
2805 for (int i = 0; i < 20; ++i) {
2806 QInputMethodEvent event;
2807 event.setCommitString("w");
2808 QGuiApplication::sendEvent(textInputObject, &event);
2809 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2810 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2812 // Delegate moved when text is removed by im.
2813 for (int i = 19; i > 1; --i) {
2814 QInputMethodEvent event;
2815 event.setCommitString(QString(), -1, 1);
2816 QGuiApplication::sendEvent(textInputObject, &event);
2817 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2818 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2820 { // Delegate moved the text is changed in place by im.
2821 QInputMethodEvent event;
2822 event.setCommitString("i", -1, 1);
2823 QGuiApplication::sendEvent(textInputObject, &event);
2824 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2825 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2828 //Test Delegate gets deleted
2829 textInputObject->setCursorDelegate(0);
2830 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2833 void tst_qquicktextinput::remoteCursorDelegate()
2835 TestHTTPServer server(SERVER_PORT);
2836 server.serveDirectory(dataDirectory());
2840 QQmlComponent component(view.engine(), QUrl(SERVER_ADDR "/RemoteCursor.qml"));
2842 view.rootContext()->setContextProperty("contextDelegate", &component);
2843 view.setSource(testFileUrl("cursorTestRemote.qml"));
2845 view.requestActivateWindow();
2846 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2847 QVERIFY(textInputObject != 0);
2849 // Delegate is created on demand, and so won't be available immediately. Focus in or
2850 // setCursorVisible(true) will trigger creation.
2851 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2852 QVERIFY(!textInputObject->isCursorVisible());
2854 textInputObject->setFocus(true);
2855 QVERIFY(textInputObject->isCursorVisible());
2857 QCOMPARE(component.status(), QQmlComponent::Loading);
2858 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2860 // Wait for component to load.
2861 QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2862 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2865 void tst_qquicktextinput::cursorVisible()
2867 QQuickTextInput input;
2868 input.componentComplete();
2869 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2871 QQuickView view(testFileUrl("cursorVisible.qml"));
2873 view.requestActivateWindow();
2874 QTest::qWaitForWindowActive(&view);
2876 QCOMPARE(input.isCursorVisible(), false);
2878 input.setCursorVisible(true);
2879 QCOMPARE(input.isCursorVisible(), true);
2880 QCOMPARE(spy.count(), 1);
2882 input.setCursorVisible(false);
2883 QCOMPARE(input.isCursorVisible(), false);
2884 QCOMPARE(spy.count(), 2);
2886 input.setFocus(true);
2887 QCOMPARE(input.isCursorVisible(), false);
2888 QCOMPARE(spy.count(), 2);
2890 input.setParentItem(view.rootObject());
2891 QCOMPARE(input.isCursorVisible(), true);
2892 QCOMPARE(spy.count(), 3);
2894 input.setFocus(false);
2895 QCOMPARE(input.isCursorVisible(), false);
2896 QCOMPARE(spy.count(), 4);
2898 input.setFocus(true);
2899 QCOMPARE(input.isCursorVisible(), true);
2900 QCOMPARE(spy.count(), 5);
2902 QWindow alternateView;
2903 alternateView.show();
2904 alternateView.requestActivateWindow();
2905 QTest::qWaitForWindowActive(&alternateView);
2907 QCOMPARE(input.isCursorVisible(), false);
2908 QCOMPARE(spy.count(), 6);
2910 view.requestActivateWindow();
2911 QTest::qWaitForWindowActive(&view);
2912 QCOMPARE(input.isCursorVisible(), true);
2913 QCOMPARE(spy.count(), 7);
2915 { // Cursor attribute with 0 length hides cursor.
2916 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2917 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2918 QCoreApplication::sendEvent(&input, &ev);
2920 QCOMPARE(input.isCursorVisible(), false);
2921 QCOMPARE(spy.count(), 8);
2923 { // Cursor attribute with non zero length shows cursor.
2924 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2925 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2926 QCoreApplication::sendEvent(&input, &ev);
2928 QCOMPARE(input.isCursorVisible(), true);
2929 QCOMPARE(spy.count(), 9);
2931 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2932 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2933 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2934 QCoreApplication::sendEvent(&input, &ev);
2936 QCOMPARE(input.isCursorVisible(), false);
2937 QCOMPARE(spy.count(), 10);
2939 input.setText("something");
2940 QCOMPARE(input.isCursorVisible(), true);
2941 QCOMPARE(spy.count(), 11);
2943 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2944 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2945 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2946 QCoreApplication::sendEvent(&input, &ev);
2948 QCOMPARE(input.isCursorVisible(), false);
2949 QCOMPARE(spy.count(), 12);
2951 input.setCursorPosition(5);
2952 QCOMPARE(input.isCursorVisible(), true);
2953 QCOMPARE(spy.count(), 13);
2956 void tst_qquicktextinput::cursorRectangle_data()
2958 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2960 QTest::addColumn<QString>("text");
2961 QTest::addColumn<int>("positionAtWidth");
2962 QTest::addColumn<int>("wrapPosition");
2963 QTest::addColumn<QString>("shortText");
2964 QTest::addColumn<bool>("leftToRight");
2966 QTest::newRow("left to right")
2967 << "Hello World!" << 5 << 11
2970 QTest::newRow("right to left")
2971 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2972 << QString::fromUtf16(arabic_str, 3)
2976 void tst_qquicktextinput::cursorRectangle()
2978 QFETCH(QString, text);
2979 QFETCH(int, positionAtWidth);
2980 QFETCH(int, wrapPosition);
2981 QFETCH(QString, shortText);
2982 QFETCH(bool, leftToRight);
2984 QQuickTextInput input;
2985 input.setText(text);
2986 input.componentComplete();
2988 QTextLayout layout(text);
2989 layout.setFont(input.font());
2990 if (!qmlDisableDistanceField()) {
2992 option.setUseDesignMetrics(true);
2993 layout.setTextOption(option);
2995 layout.beginLayout();
2996 QTextLine line = layout.createLine();
3001 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
3003 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
3004 offset = line.horizontalAdvance() - input.width();
3006 input.setHeight(qCeil(line.height() * 3 / 2));
3010 for (int i = 0; i <= positionAtWidth; ++i) {
3011 input.setCursorPosition(i);
3012 r = input.cursorRectangle();
3014 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
3015 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3016 QCOMPARE(input.positionToRectangle(i), r);
3019 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
3020 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3022 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
3023 input.setCursorPosition(i);
3024 QCOMPARE(r, input.cursorRectangle());
3025 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3026 QCOMPARE(input.positionToRectangle(i), r);
3029 for (int i = text.length() - 2; i >= 0; --i) {
3030 input.setCursorPosition(i);
3031 r = input.cursorRectangle();
3032 QCOMPARE(r.top(), 0.);
3034 QVERIFY(r.right() >= 0);
3036 QVERIFY(r.left() <= input.width());
3038 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3039 QCOMPARE(input.positionToRectangle(i), r);
3042 // Check position with word wrap.
3043 input.setWrapMode(QQuickTextInput::WordWrap);
3044 input.setAutoScroll(false);
3045 for (int i = 0; i < wrapPosition; ++i) {
3046 input.setCursorPosition(i);
3047 r = input.cursorRectangle();
3049 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
3050 QCOMPARE(r.top(), 0.);
3051 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3052 QCOMPARE(input.positionToRectangle(i), r);
3055 input.setCursorPosition(wrapPosition);
3056 r = input.cursorRectangle();
3058 QCOMPARE(r.left(), 0.);
3060 QCOMPARE(r.left(), input.width());
3062 QVERIFY(r.top() >= line.height() - 1);
3063 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3064 QCOMPARE(input.positionToRectangle(11), r);
3066 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3067 input.setCursorPosition(i);
3068 r = input.cursorRectangle();
3069 QVERIFY(r.top() >= line.height() - 1);
3070 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3071 QCOMPARE(input.positionToRectangle(i), r);
3074 // Check vertical scrolling with word wrap.
3075 input.setAutoScroll(true);
3076 for (int i = 0; i <= positionAtWidth; ++i) {
3077 input.setCursorPosition(i);
3078 r = input.cursorRectangle();
3080 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
3081 QCOMPARE(r.top(), 0.);
3082 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3083 QCOMPARE(input.positionToRectangle(i), r);
3086 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
3087 // reaches the next non-whitespace character.
3088 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3089 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
3090 input.setCursorPosition(i);
3091 QCOMPARE(r, input.cursorRectangle());
3092 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3093 QCOMPARE(input.positionToRectangle(i), r);
3096 input.setCursorPosition(wrapPosition);
3097 r = input.cursorRectangle();
3099 QCOMPARE(r.left(), 0.);
3101 QCOMPARE(r.left(), input.width());
3103 QVERIFY(r.bottom() >= input.height());
3104 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3105 QCOMPARE(input.positionToRectangle(11), r);
3107 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3108 input.setCursorPosition(i);
3109 r = input.cursorRectangle();
3110 QVERIFY(r.bottom() >= input.height());
3111 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3112 QCOMPARE(input.positionToRectangle(i), r);
3115 for (int i = text.length() - 2; i >= wrapPosition; --i) {
3116 input.setCursorPosition(i);
3117 r = input.cursorRectangle();
3118 QVERIFY(r.bottom() >= input.height());
3119 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3120 QCOMPARE(input.positionToRectangle(i), r);
3123 input.setCursorPosition(wrapPosition - 1);
3124 r = input.cursorRectangle();
3125 QCOMPARE(r.top(), 0.);
3126 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3127 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3128 QCOMPARE(input.positionToRectangle(10), r);
3130 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
3131 input.setCursorPosition(i);
3132 QCOMPARE(r, input.cursorRectangle());
3133 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3134 QCOMPARE(input.positionToRectangle(i), r);
3137 for (int i = positionAtWidth; i >= 0; --i) {
3138 input.setCursorPosition(i);
3139 r = input.cursorRectangle();
3140 QCOMPARE(r.top(), 0.);
3141 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3142 QCOMPARE(input.positionToRectangle(i), r);
3145 input.setText(shortText);
3146 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
3147 r = input.cursorRectangle();
3148 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3151 void tst_qquicktextinput::readOnly()
3153 QQuickView window(testFileUrl("readOnly.qml"));
3155 window.requestActivateWindow();
3157 QVERIFY(window.rootObject() != 0);
3159 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3161 QVERIFY(input != 0);
3162 QTRY_VERIFY(input->hasActiveFocus() == true);
3163 QVERIFY(input->isReadOnly() == true);
3164 QString initial = input->text();
3165 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
3166 simulateKey(&window, k);
3167 simulateKey(&window, Qt::Key_Return);
3168 simulateKey(&window, Qt::Key_Space);
3169 simulateKey(&window, Qt::Key_Escape);
3170 QCOMPARE(input->text(), initial);
3172 input->setCursorPosition(3);
3173 input->setReadOnly(false);
3174 QCOMPARE(input->isReadOnly(), false);
3175 QCOMPARE(input->cursorPosition(), input->text().length());
3178 void tst_qquicktextinput::echoMode()
3180 QQuickView window(testFileUrl("echoMode.qml"));
3182 window.requestActivateWindow();
3183 QTest::qWaitForWindowActive(&window);
3185 QVERIFY(window.rootObject() != 0);
3187 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3189 QVERIFY(input != 0);
3190 QTRY_VERIFY(input->hasActiveFocus() == true);
3191 QString initial = input->text();
3192 Qt::InputMethodHints ref;
3193 QCOMPARE(initial, QLatin1String("ABCDefgh"));
3194 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
3195 QCOMPARE(input->displayText(), input->text());
3197 ref &= ~Qt::ImhHiddenText;
3198 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3199 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3200 input->setEchoMode(QQuickTextInput::NoEcho);
3201 QCOMPARE(input->text(), initial);
3202 QCOMPARE(input->displayText(), QLatin1String(""));
3203 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
3205 ref |= Qt::ImhHiddenText;
3206 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3207 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3208 input->setEchoMode(QQuickTextInput::Password);
3210 ref |= Qt::ImhHiddenText;
3211 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3212 QCOMPARE(input->text(), initial);
3213 QCOMPARE(input->displayText(), QLatin1String("********"));
3214 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3215 // clearing input hints do not clear bits set by echo mode
3216 input->setInputMethodHints(Qt::ImhNone);
3217 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3218 input->setPasswordCharacter(QChar('Q'));
3219 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
3220 QCOMPARE(input->text(), initial);
3221 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3222 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
3223 //PasswordEchoOnEdit
3224 ref &= ~Qt::ImhHiddenText;
3225 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3226 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3227 QCOMPARE(input->text(), initial);
3228 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3229 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
3230 QTest::keyPress(&window, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
3231 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
3232 QCOMPARE(input->text(), QLatin1String("a"));
3233 QCOMPARE(input->displayText(), QLatin1String("a"));
3234 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
3235 input->setFocus(false);
3236 QVERIFY(input->hasActiveFocus() == false);
3237 QCOMPARE(input->displayText(), QLatin1String("Q"));
3238 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
3239 input->setFocus(true);
3240 QVERIFY(input->hasActiveFocus());
3241 QInputMethodEvent inputEvent;
3242 inputEvent.setCommitString(initial);
3243 QGuiApplication::sendEvent(input, &inputEvent);
3244 QCOMPARE(input->text(), initial);
3245 QCOMPARE(input->displayText(), initial);
3246 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
3249 void tst_qquicktextinput::passwordEchoDelay()
3251 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
3253 QSKIP("No mask delay in use");
3254 QQuickView window(testFileUrl("echoMode.qml"));
3256 window.requestActivateWindow();
3257 QTest::qWaitForWindowActive(&window);
3259 QVERIFY(window.rootObject() != 0);
3261 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3263 QVERIFY(input->hasActiveFocus());
3265 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3268 QChar fillChar = QLatin1Char('*');
3270 input->setEchoMode(QQuickTextInput::Password);
3271 QCOMPARE(input->displayText(), QString(8, fillChar));
3272 input->setText(QString());
3273 QCOMPARE(input->displayText(), QString());
3275 QTest::keyPress(&window, '0');
3276 QTest::keyPress(&window, '1');
3277 QTest::keyPress(&window, '2');
3278 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
3279 QTest::keyPress(&window, '3');
3280 QTest::keyPress(&window, '4');
3281 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3282 QTest::keyPress(&window, Qt::Key_Backspace);
3283 QCOMPARE(input->displayText(), QString(4, fillChar));
3284 QTest::keyPress(&window, '4');
3285 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3286 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3288 // Verify the last character entered is replaced by the fill character after a delay.
3289 // Also check the cursor position is updated to accomdate a size difference between
3290 // the fill character and the replaced character.
3291 QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged()));
3292 QTest::qWait(maskDelay);
3293 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
3294 QCOMPARE(cursorSpy.count(), 1);
3295 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3297 QTest::keyPress(&window, '5');
3298 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3299 input->setFocus(false);
3300 QVERIFY(!input->hasFocus());
3301 QCOMPARE(input->displayText(), QString(6, fillChar));
3302 input->setFocus(true);
3303 QTRY_VERIFY(input->hasFocus());
3304 QCOMPARE(input->displayText(), QString(6, fillChar));
3305 QTest::keyPress(&window, '6');
3306 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3308 QInputMethodEvent ev;
3309 ev.setCommitString(QLatin1String("7"));
3310 QGuiApplication::sendEvent(input, &ev);
3311 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3313 input->setCursorPosition(3);
3314 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3315 QTest::keyPress(&window, 'a');
3316 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3317 QTest::keyPress(&window, Qt::Key_Backspace);
3318 QCOMPARE(input->displayText(), QString(8, fillChar));
3322 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3324 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3325 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3327 QGuiApplication::sendEvent(view, &press);
3328 QGuiApplication::sendEvent(view, &release);
3332 void tst_qquicktextinput::focusOnPress()
3334 QString componentStr =
3335 "import QtQuick 2.0\n"
3337 "property bool selectOnFocus: false\n"
3338 "width: 100; height: 50\n"
3339 "activeFocusOnPress: true\n"
3340 "text: \"Hello World\"\n"
3341 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3343 QQmlComponent texteditComponent(&engine);
3344 texteditComponent.setData(componentStr.toLatin1(), QUrl());
3345 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3346 QVERIFY(textInputObject != 0);
3347 QCOMPARE(textInputObject->focusOnPress(), true);
3348 QCOMPARE(textInputObject->hasFocus(), false);
3350 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3351 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3352 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3354 textInputObject->setFocusOnPress(true);
3355 QCOMPARE(textInputObject->focusOnPress(), true);
3356 QCOMPARE(activeFocusOnPressSpy.count(), 0);
3358 QQuickWindow window;
3359 window.resize(100, 50);
3360 textInputObject->setParentItem(window.rootItem());
3362 window.requestActivateWindow();
3363 QTest::qWaitForWindowActive(&window);
3365 QCOMPARE(textInputObject->hasFocus(), false);
3366 QCOMPARE(textInputObject->hasActiveFocus(), false);
3368 QPoint centerPoint(window.width()/2, window.height()/2);
3369 Qt::KeyboardModifiers noModifiers = 0;
3370 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3371 QGuiApplication::processEvents();
3372 QCOMPARE(textInputObject->hasFocus(), true);
3373 QCOMPARE(textInputObject->hasActiveFocus(), true);
3374 QCOMPARE(focusSpy.count(), 1);
3375 QCOMPARE(activeFocusSpy.count(), 1);
3376 QCOMPARE(textInputObject->selectedText(), QString());
3377 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3379 textInputObject->setFocusOnPress(false);
3380 QCOMPARE(textInputObject->focusOnPress(), false);
3381 QCOMPARE(activeFocusOnPressSpy.count(), 1);
3383 textInputObject->setFocus(false);
3384 QCOMPARE(textInputObject->hasFocus(), false);
3385 QCOMPARE(textInputObject->hasActiveFocus(), false);
3386 QCOMPARE(focusSpy.count(), 2);
3387 QCOMPARE(activeFocusSpy.count(), 2);
3389 // Wait for double click timeout to expire before clicking again.
3391 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3392 QGuiApplication::processEvents();
3393 QCOMPARE(textInputObject->hasFocus(), false);
3394 QCOMPARE(textInputObject->hasActiveFocus(), false);
3395 QCOMPARE(focusSpy.count(), 2);
3396 QCOMPARE(activeFocusSpy.count(), 2);
3397 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3399 textInputObject->setFocusOnPress(true);
3400 QCOMPARE(textInputObject->focusOnPress(), true);
3401 QCOMPARE(activeFocusOnPressSpy.count(), 2);
3403 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3404 textInputObject->setProperty("selectOnFocus", true);
3407 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3408 QGuiApplication::processEvents();
3409 QCOMPARE(textInputObject->hasFocus(), true);
3410 QCOMPARE(textInputObject->hasActiveFocus(), true);
3411 QCOMPARE(focusSpy.count(), 3);
3412 QCOMPARE(activeFocusSpy.count(), 3);
3413 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3414 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3417 void tst_qquicktextinput::openInputPanel()
3419 PlatformInputContext platformInputContext;
3420 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3421 inputMethodPrivate->testContext = &platformInputContext;
3423 QQuickView view(testFileUrl("openInputPanel.qml"));
3425 view.requestActivateWindow();
3426 QTest::qWaitForWindowActive(&view);
3428 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3431 // check default values
3432 QVERIFY(input->focusOnPress());
3433 QVERIFY(!input->hasActiveFocus());
3434 QVERIFY(qApp->focusObject() != input);
3435 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3437 // input panel should open on focus
3438 QPoint centerPoint(view.width()/2, view.height()/2);
3439 Qt::KeyboardModifiers noModifiers = 0;
3440 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3441 QGuiApplication::processEvents();
3442 QVERIFY(input->hasActiveFocus());
3443 QCOMPARE(qApp->focusObject(), input);
3444 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3445 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3447 // input panel should be re-opened when pressing already focused TextInput
3448 qApp->inputMethod()->hide();
3449 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3450 QVERIFY(input->hasActiveFocus());
3451 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3452 QGuiApplication::processEvents();
3453 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3454 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3456 // input panel should stay visible if focus is lost to another text inputor
3457 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3458 QQuickTextInput anotherInput;
3459 anotherInput.componentComplete();
3460 anotherInput.setParentItem(view.rootObject());
3461 anotherInput.setFocus(true);
3462 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3463 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3464 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3466 anotherInput.setFocus(false);
3467 QVERIFY(qApp->focusObject() != &anotherInput);
3468 QCOMPARE(view.activeFocusItem(), view.rootItem());
3469 anotherInput.setFocus(true);
3471 qApp->inputMethod()->hide();
3473 // input panel should not be opened if TextInput is read only
3474 input->setReadOnly(true);
3475 input->setFocus(true);
3476 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3477 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3478 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3479 QGuiApplication::processEvents();
3480 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3482 // input panel should not be opened if focusOnPress is set to false
3483 input->setFocusOnPress(false);
3484 input->setFocus(false);
3485 input->setFocus(true);
3486 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3487 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3488 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3489 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3492 class MyTextInput : public QQuickTextInput
3495 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3499 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3502 return QQuickTextInput::updatePaintNode(node, data);
3507 void tst_qquicktextinput::setHAlignClearCache()
3511 input.setText("Hello world");
3512 input.setParentItem(view.rootItem());
3514 view.requestActivateWindow();
3515 QTest::qWaitForWindowActive(&view);
3517 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3519 QTRY_COMPARE(input.nbPaint, 1);
3520 input.setHAlign(QQuickTextInput::AlignRight);
3521 //Changing the alignment should trigger a repaint
3522 QTRY_COMPARE(input.nbPaint, 2);
3525 void tst_qquicktextinput::focusOutClearSelection()
3528 QQuickTextInput input;
3529 QQuickTextInput input2;
3530 input.setText(QLatin1String("Hello world"));
3531 input.setFocus(true);
3532 input2.setParentItem(view.rootItem());
3533 input.setParentItem(view.rootItem());
3534 input.componentComplete();
3535 input2.componentComplete();
3537 view.requestActivateWindow();
3538 QTest::qWaitForWindowActive(&view);
3539 QVERIFY(input.hasActiveFocus());
3541 //The selection should work
3542 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3543 input2.setFocus(true);
3544 QGuiApplication::processEvents();
3545 //The input lost the focus selection should be cleared
3546 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3549 void tst_qquicktextinput::geometrySignals()
3551 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3552 QObject *o = component.create();
3554 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3555 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3559 void tst_qquicktextinput::contentSize()
3561 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3562 QQmlComponent textComponent(&engine);
3563 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3564 QScopedPointer<QObject> object(textComponent.create());
3565 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3567 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3569 textObject->setText("The quick red fox jumped over the lazy brown dog");
3571 QVERIFY(textObject->contentWidth() > textObject->width());
3572 QVERIFY(textObject->contentHeight() < textObject->height());
3573 QCOMPARE(spy.count(), 1);
3575 textObject->setWrapMode(QQuickTextInput::WordWrap);
3576 QVERIFY(textObject->contentWidth() <= textObject->width());
3577 QVERIFY(textObject->contentHeight() > textObject->height());
3578 QCOMPARE(spy.count(), 2);
3580 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3582 QVERIFY(textObject->contentWidth() > textObject->width());
3583 QVERIFY(textObject->contentHeight() > textObject->height());
3584 QCOMPARE(spy.count(), 3);
3586 textObject->setText("The quick red fox jumped over the lazy brown dog");
3587 for (int w = 60; w < 120; ++w) {
3588 textObject->setWidth(w);
3589 QVERIFY(textObject->contentWidth() <= textObject->width());
3590 QVERIFY(textObject->contentHeight() > textObject->height());
3594 static void sendPreeditText(QQuickItem *item, const QString &text, int cursor)
3596 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3597 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3598 QCoreApplication::sendEvent(item, &event);
3601 void tst_qquicktextinput::preeditAutoScroll()
3603 QString preeditText = "califragisiticexpialidocious!";
3605 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3607 view.requestActivateWindow();
3608 QTest::qWaitForWindowActive(&view);
3609 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3611 QVERIFY(input->hasActiveFocus());
3613 input->setWidth(input->implicitWidth());
3615 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3616 int cursorRectangleChanges = 0;
3618 // test the text is scrolled so the preedit is visible.
3619 sendPreeditText(input, preeditText.mid(0, 3), 1);
3620 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3621 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3622 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3624 // test the text is scrolled back when the preedit is removed.
3625 QInputMethodEvent imEvent;
3626 QCoreApplication::sendEvent(input, &imEvent);
3627 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3628 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3629 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3631 QTextLayout layout(preeditText);
3632 layout.setFont(input->font());
3633 if (!qmlDisableDistanceField()) {
3635 option.setUseDesignMetrics(true);
3636 layout.setTextOption(option);
3638 layout.beginLayout();
3639 QTextLine line = layout.createLine();
3642 // test if the preedit is larger than the text input that the
3643 // character preceding the cursor is still visible.
3644 qreal x = input->positionToRectangle(0).x();
3645 for (int i = 0; i < 3; ++i) {
3646 sendPreeditText(input, preeditText, i + 1);
3647 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3648 QVERIFY(input->cursorRectangle().right() >= width - 3);
3649 QVERIFY(input->positionToRectangle(0).x() < x);
3650 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3651 x = input->positionToRectangle(0).x();
3653 for (int i = 1; i >= 0; --i) {
3654 sendPreeditText(input, preeditText, i + 1);
3655 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3656 QVERIFY(input->cursorRectangle().right() >= width - 3);
3657 QVERIFY(input->positionToRectangle(0).x() > x);
3658 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3659 x = input->positionToRectangle(0).x();
3662 // Test incrementing the preedit cursor doesn't cause further
3663 // scrolling when right most text is visible.
3664 sendPreeditText(input, preeditText, preeditText.length() - 3);
3665 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3666 x = input->positionToRectangle(0).x();
3667 for (int i = 2; i >= 0; --i) {
3668 sendPreeditText(input, preeditText, preeditText.length() - i);
3669 QCOMPARE(input->positionToRectangle(0).x(), x);
3670 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3672 for (int i = 1; i < 3; ++i) {
3673 sendPreeditText(input, preeditText, preeditText.length() - i);
3674 QCOMPARE(input->positionToRectangle(0).x(), x);
3675 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3678 // Test disabling auto scroll.
3679 QCoreApplication::sendEvent(input, &imEvent);
3681 input->setAutoScroll(false);
3682 sendPreeditText(input, preeditText.mid(0, 3), 1);
3683 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3684 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3687 void tst_qquicktextinput::preeditCursorRectangle()
3689 QString preeditText = "super";
3691 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3693 view.requestActivateWindow();
3694 QTest::qWaitForWindowActive(&view);
3695 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3697 QVERIFY(input->hasActiveFocus());
3699 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3704 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3705 QCoreApplication::sendEvent(input, &query);
3706 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3708 // Verify that the micro focus rect is positioned the same for position 0 as
3709 // it would be if there was no preedit text.
3710 sendPreeditText(input, preeditText, 0);
3711 QCoreApplication::sendEvent(input, &query);
3712 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3713 QCOMPARE(currentRect, previousRect);
3714 QCOMPARE(input->cursorRectangle(), currentRect);
3715 QCOMPARE(cursor->pos(), currentRect.topLeft());
3717 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3718 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3720 // Verify that the micro focus rect moves to the left as the cursor position
3722 for (int i = 1; i <= 5; ++i) {
3723 sendPreeditText(input, preeditText, i);
3724 QCoreApplication::sendEvent(input, &query);
3725 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3726 QVERIFY(previousRect.left() < currentRect.left());
3727 QCOMPARE(input->cursorRectangle(), currentRect);
3728 QCOMPARE(cursor->pos(), currentRect.topLeft());
3729 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3730 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3731 previousRect = currentRect;
3734 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3735 // but the (non-zero) cursor position is the same.
3738 sendPreeditText(input, "wwwww", 5);
3739 QCoreApplication::sendEvent(input, &query);
3740 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3741 QCOMPARE(input->cursorRectangle(), currentRect);
3742 QCOMPARE(cursor->pos(), currentRect.topLeft());
3743 QCOMPARE(inputSpy.count(), 1);
3744 QCOMPARE(panelSpy.count(), 1);
3746 // Verify that if there is no preedit cursor then the micro focus rect is the
3747 // same as it would be if it were positioned at the end of the preedit text.
3750 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3751 QCoreApplication::sendEvent(input, &imEvent); }
3752 QCoreApplication::sendEvent(input, &query);
3753 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3754 QCOMPARE(currentRect, previousRect);
3755 QCOMPARE(input->cursorRectangle(), currentRect);
3756 QCOMPARE(cursor->pos(), currentRect.topLeft());
3757 QCOMPARE(inputSpy.count(), 1);
3758 QCOMPARE(panelSpy.count(), 1);
3761 void tst_qquicktextinput::inputContextMouseHandler()
3763 PlatformInputContext platformInputContext;
3764 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3765 inputMethodPrivate->testContext = &platformInputContext;
3767 QString text = "supercalifragisiticexpialidocious!";
3768 QQuickView view(testFileUrl("inputContext.qml"));
3769 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3772 input->setFocus(true);
3776 view.requestActivateWindow();
3777 QTest::qWaitForWindowActive(&view);
3779 QTextLayout layout(text);
3780 layout.setFont(input->font());
3781 if (!qmlDisableDistanceField()) {
3783 option.setUseDesignMetrics(true);
3784 layout.setTextOption(option);
3786 layout.beginLayout();
3787 QTextLine line = layout.createLine();
3790 const qreal x = line.cursorToX(2, QTextLine::Leading);
3791 const qreal y = line.height() / 2;
3792 QPoint position = QPointF(x, y).toPoint();
3794 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3795 QGuiApplication::sendEvent(input, &inputEvent);
3797 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3798 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3799 QGuiApplication::processEvents();
3801 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3802 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3803 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3806 void tst_qquicktextinput::inputMethodComposing()
3808 QString text = "supercalifragisiticexpialidocious!";
3810 QQuickView view(testFileUrl("inputContext.qml"));
3812 view.requestActivateWindow();
3813 QTest::qWaitForWindowActive(&view);
3814 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3816 QVERIFY(input->hasActiveFocus());
3817 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3819 QCOMPARE(input->isInputMethodComposing(), false);
3821 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3822 QGuiApplication::sendEvent(input, &event);
3824 QCOMPARE(input->isInputMethodComposing(), true);
3825 QCOMPARE(spy.count(), 1);
3828 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3829 QGuiApplication::sendEvent(input, &event);
3831 QCOMPARE(spy.count(), 1);
3834 QInputMethodEvent event;
3835 QGuiApplication::sendEvent(input, &event);
3837 QCOMPARE(input->isInputMethodComposing(), false);
3838 QCOMPARE(spy.count(), 2);
3840 // Changing the text while not composing doesn't alter the composing state.
3841 input->setText(text.mid(0, 16));
3842 QCOMPARE(input->isInputMethodComposing(), false);
3843 QCOMPARE(spy.count(), 2);
3846 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3847 QGuiApplication::sendEvent(input, &event);
3849 QCOMPARE(input->isInputMethodComposing(), true);
3850 QCOMPARE(spy.count(), 3);
3852 // Changing the text while composing cancels composition.
3853 input->setText(text.mid(0, 12));
3854 QCOMPARE(input->isInputMethodComposing(), false);
3855 QCOMPARE(spy.count(), 4);
3857 { // Preedit cursor positioned outside (empty) preedit; composing.
3858 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3859 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3860 QGuiApplication::sendEvent(input, &event);
3862 QCOMPARE(input->isInputMethodComposing(), true);
3863 QCOMPARE(spy.count(), 5);
3866 { // Cursor hidden; composing
3867 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3868 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3869 QGuiApplication::sendEvent(input, &event);
3871 QCOMPARE(input->isInputMethodComposing(), true);
3872 QCOMPARE(spy.count(), 5);
3874 { // Default cursor attributes; composing.
3875 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3876 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3877 QGuiApplication::sendEvent(input, &event);
3879 QCOMPARE(input->isInputMethodComposing(), true);
3880 QCOMPARE(spy.count(), 5);
3882 { // Selections are persisted: not composing
3883 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3884 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3885 QGuiApplication::sendEvent(input, &event);
3887 QCOMPARE(input->isInputMethodComposing(), false);
3888 QCOMPARE(spy.count(), 6);
3890 input->setCursorPosition(12);
3892 { // Formatting applied; composing.
3893 QTextCharFormat format;
3894 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3895 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3896 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3897 QGuiApplication::sendEvent(input, &event);
3899 QCOMPARE(input->isInputMethodComposing(), true);
3900 QCOMPARE(spy.count(), 7);
3903 QInputMethodEvent event;
3904 QGuiApplication::sendEvent(input, &event);
3906 QCOMPARE(input->isInputMethodComposing(), false);
3907 QCOMPARE(spy.count(), 8);
3910 void tst_qquicktextinput::inputMethodUpdate()
3912 PlatformInputContext platformInputContext;
3913 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3914 inputMethodPrivate->testContext = &platformInputContext;
3916 QQuickView view(testFileUrl("inputContext.qml"));
3918 view.requestActivateWindow();
3919 QTest::qWaitForWindowActive(&view);
3920 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3922 QVERIFY(input->hasActiveFocus());
3924 // text change even without cursor position change needs to trigger update
3925 input->setText("test");
3926 platformInputContext.clear();
3927 input->setText("xxxx");
3928 QVERIFY(platformInputContext.m_updateCallCount > 0);
3930 // input method event replacing text
3931 platformInputContext.clear();
3933 QInputMethodEvent inputMethodEvent;
3934 inputMethodEvent.setCommitString("y", -1, 1);
3935 QGuiApplication::sendEvent(input, &inputMethodEvent);
3937 QVERIFY(platformInputContext.m_updateCallCount > 0);
3939 // input method changing selection
3940 platformInputContext.clear();
3942 QList<QInputMethodEvent::Attribute> attributes;
3943 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3944 QInputMethodEvent inputMethodEvent("", attributes);
3945 QGuiApplication::sendEvent(input, &inputMethodEvent);
3947 QVERIFY(input->selectionStart() != input->selectionEnd());
3948 QVERIFY(platformInputContext.m_updateCallCount > 0);
3950 // programmatical selections trigger update
3951 platformInputContext.clear();
3953 QVERIFY(platformInputContext.m_updateCallCount > 0);
3956 platformInputContext.clear();
3957 QFont font = input->font();
3958 font.setBold(!font.bold());
3959 input->setFont(font);
3960 QVERIFY(platformInputContext.m_updateCallCount > 0);
3963 platformInputContext.clear();
3965 QInputMethodEvent inputMethodEvent;
3966 inputMethodEvent.setCommitString("y");
3967 QGuiApplication::sendEvent(input, &inputMethodEvent);
3969 QVERIFY(platformInputContext.m_updateCallCount > 0);
3971 // changing cursor position
3972 platformInputContext.clear();
3973 input->setCursorPosition(0);
3974 QVERIFY(platformInputContext.m_updateCallCount > 0);
3976 // read only disabled input method
3977 platformInputContext.clear();
3978 input->setReadOnly(true);
3979 QVERIFY(platformInputContext.m_updateCallCount > 0);
3980 input->setReadOnly(false);
3982 // no updates while no focus
3983 input->setFocus(false);
3984 platformInputContext.clear();
3985 input->setText("Foo");
3986 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3987 input->setCursorPosition(1);
3988 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3990 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3991 input->setReadOnly(true);
3992 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3995 void tst_qquicktextinput::cursorRectangleSize()
3997 QQuickView *window = new QQuickView(testFileUrl("positionAt.qml"));
3998 QVERIFY(window->rootObject() != 0);
3999 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(window->rootObject());
4001 // make sure cursor rectangle is not at (0,0)
4002 textInput->setX(10);
4003 textInput->setY(10);
4004 textInput->setCursorPosition(3);
4005 QVERIFY(textInput != 0);
4006 textInput->setFocus(true);
4008 window->requestActivateWindow();
4009 QTest::qWaitForWindowActive(window);
4010 QVERIFY(textInput->hasActiveFocus());
4012 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
4013 qApp->sendEvent(textInput, &event);
4014 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
4016 QRectF cursorRectFromItem = textInput->cursorRectangle();
4017 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
4019 // item and input query cursor rectangles match
4020 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
4022 // item cursor rectangle and positionToRectangle calculations match
4023 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
4025 // item-window transform and input item transform match
4026 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToWindowTransform(), qApp->inputMethod()->inputItemTransform());
4028 // input panel cursorRectangle property and tranformed item cursor rectangle match
4029 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToWindowTransform().mapRect(cursorRectFromItem);
4030 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
4035 void tst_qquicktextinput::tripleClickSelectsAll()
4037 QString qmlfile = testFile("positionAt.qml");
4038 QQuickView view(QUrl::fromLocalFile(qmlfile));
4040 view.requestActivateWindow();
4041 QTest::qWaitForWindowActive(&view);
4043 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
4046 QLatin1String hello("Hello world!");
4047 input->setSelectByMouse(true);
4048 input->setText(hello);
4050 // Clicking on the same point inside TextInput three times in a row
4051 // should trigger a triple click, thus selecting all the text.
4052 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
4053 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4054 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
4055 QGuiApplication::processEvents();
4056 QCOMPARE(input->selectedText(), hello);
4058 // Now it simulates user moving the mouse between the second and the third click.
4059 // In this situation, we don't expect a triple click.
4060 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
4061 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4062 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
4063 QGuiApplication::processEvents();
4064 QVERIFY(input->selectedText().isEmpty());
4066 // And now we press the third click too late, so no triple click event is triggered.
4067 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4068 QGuiApplication::processEvents();
4069 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
4070 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
4071 QGuiApplication::processEvents();
4072 QVERIFY(input->selectedText().isEmpty());
4075 void tst_qquicktextinput::QTBUG_19956_data()
4077 QTest::addColumn<QString>("url");
4078 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
4079 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
4083 void tst_qquicktextinput::getText_data()
4085 QTest::addColumn<QString>("text");
4086 QTest::addColumn<QString>("inputMask");
4087 QTest::addColumn<int>("start");
4088 QTest::addColumn<int>("end");
4089 QTest::addColumn<QString>("expectedText");
4091 QTest::newRow("all plain text")
4094 << 0 << standard.at(0).length()
4097 QTest::newRow("plain text sub string")
4101 << standard.at(0).mid(0, 12);
4103 QTest::newRow("plain text sub string reversed")
4107 << standard.at(0).mid(0, 12);
4109 QTest::newRow("plain text cropped beginning")
4113 << standard.at(0).mid(0, 4);
4115 QTest::newRow("plain text cropped end")
4118 << 23 << standard.at(0).length() + 8
4119 << standard.at(0).mid(23);
4121 QTest::newRow("plain text cropped beginning and end")
4124 << -9 << standard.at(0).length() + 4
4128 void tst_qquicktextinput::getText()
4130 QFETCH(QString, text);
4131 QFETCH(QString, inputMask);
4134 QFETCH(QString, expectedText);
4136 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4137 QQmlComponent textInputComponent(&engine);
4138 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4139 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4140 QVERIFY(textInput != 0);
4142 QCOMPARE(textInput->getText(start, end), expectedText);
4145 void tst_qquicktextinput::insert_data()
4147 QTest::addColumn<QString>("text");
4148 QTest::addColumn<QString>("inputMask");
4149 QTest::addColumn<int>("selectionStart");
4150 QTest::addColumn<int>("selectionEnd");
4151 QTest::addColumn<int>("insertPosition");
4152 QTest::addColumn<QString>("insertText");
4153 QTest::addColumn<QString>("expectedText");
4154 QTest::addColumn<int>("expectedSelectionStart");
4155 QTest::addColumn<int>("expectedSelectionEnd");
4156 QTest::addColumn<int>("expectedCursorPosition");
4157 QTest::addColumn<bool>("selectionChanged");
4158 QTest::addColumn<bool>("cursorPositionChanged");
4160 QTest::newRow("at cursor position (beginning)")
4165 << QString("Hello") + standard.at(0)
4169 QTest::newRow("at cursor position (end)")
4172 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
4174 << standard.at(0) + QString("Hello")
4175 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4178 QTest::newRow("at cursor position (middle)")
4183 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4187 QTest::newRow("after cursor position (beginning)")
4192 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4196 QTest::newRow("before cursor position (end)")
4199 << standard.at(0).length() << standard.at(0).length() << 18
4201 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4202 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4205 QTest::newRow("before cursor position (middle)")
4210 << QString("Hello") + standard.at(0)
4214 QTest::newRow("after cursor position (middle)")
4217 << 18 << 18 << standard.at(0).length()
4219 << standard.at(0) + QString("Hello")
4223 QTest::newRow("before selection")
4228 << QString("Hello") + standard.at(0)
4232 QTest::newRow("before reversed selection")
4237 << QString("Hello") + standard.at(0)
4241 QTest::newRow("after selection")
4244 << 14 << 19 << standard.at(0).length()
4246 << standard.at(0) + QString("Hello")
4250 QTest::newRow("after reversed selection")
4253 << 19 << 14 << standard.at(0).length()
4255 << standard.at(0) + QString("Hello")
4259 QTest::newRow("into selection")
4264 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4268 QTest::newRow("into reversed selection")
4273 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4277 QTest::newRow("rich text into plain text")
4281 << QString("<b>Hello</b>")
4282 << QString("<b>Hello</b>") + standard.at(0)
4286 QTest::newRow("before start")
4295 QTest::newRow("past end")
4298 << 0 << 0 << standard.at(0).length() + 3
4304 const QString inputMask = "009.009.009.009";
4305 const QString ip = "192.168.2.14";
4307 QTest::newRow("mask: at cursor position (beginning)")
4312 << QString("125.168.2.14")
4316 QTest::newRow("mask: at cursor position (end)")
4319 << inputMask.length() << inputMask.length() << inputMask.length()
4322 << inputMask.length() << inputMask.length() << inputMask.length()
4325 QTest::newRow("mask: at cursor position (middle)")
4330 << QString("192.167.5.24")
4334 QTest::newRow("mask: after cursor position (beginning)")
4339 << QString("192.167.5.24")
4343 QTest::newRow("mask: before cursor position (end)")
4346 << inputMask.length() << inputMask.length() << 6
4348 << QString("192.167.5.24")
4349 << inputMask.length() << inputMask.length() << inputMask.length()
4352 QTest::newRow("mask: before cursor position (middle)")
4357 << QString("125.168.2.14")
4361 QTest::newRow("mask: after cursor position (middle)")
4370 QTest::newRow("mask: before selection")
4375 << QString("125.168.2.14")
4379 QTest::newRow("mask: before reversed selection")
4384 << QString("125.168.2.14")
4388 QTest::newRow("mask: after selection")
4397 QTest::newRow("mask: after reversed selection")
4406 QTest::newRow("mask: into selection")
4411 << QString("192.167.5.24")
4415 QTest::newRow("mask: into reversed selection")
4420 << QString("192.167.5.24")
4424 QTest::newRow("mask: before start")
4433 QTest::newRow("mask: past end")
4436 << 0 << 0 << ip.length() + 3
4442 QTest::newRow("mask: invalid characters")
4447 << QString("192.168.2.14")
4451 QTest::newRow("mask: mixed validity")
4455 << QString("a1b2c5")
4456 << QString("125.168.2.14")
4461 void tst_qquicktextinput::insert()
4463 QFETCH(QString, text);
4464 QFETCH(QString, inputMask);
4465 QFETCH(int, selectionStart);
4466 QFETCH(int, selectionEnd);
4467 QFETCH(int, insertPosition);
4468 QFETCH(QString, insertText);
4469 QFETCH(QString, expectedText);
4470 QFETCH(int, expectedSelectionStart);
4471 QFETCH(int, expectedSelectionEnd);
4472 QFETCH(int, expectedCursorPosition);
4473 QFETCH(bool, selectionChanged);
4474 QFETCH(bool, cursorPositionChanged);
4476 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4477 QQmlComponent textInputComponent(&engine);
4478 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4479 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4480 QVERIFY(textInput != 0);
4482 textInput->select(selectionStart, selectionEnd);
4484 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4485 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4486 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4487 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4488 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4490 textInput->insert(insertPosition, insertText);
4492 QCOMPARE(textInput->text(), expectedText);
4493 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4495 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4496 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4497 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4499 if (selectionStart > selectionEnd)
4500 qSwap(selectionStart, selectionEnd);
4502 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4503 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4504 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4505 QCOMPARE(textSpy.count() > 0, text != expectedText);
4506 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4509 void tst_qquicktextinput::remove_data()
4511 QTest::addColumn<QString>("text");
4512 QTest::addColumn<QString>("inputMask");
4513 QTest::addColumn<int>("selectionStart");
4514 QTest::addColumn<int>("selectionEnd");
4515 QTest::addColumn<int>("removeStart");
4516 QTest::addColumn<int>("removeEnd");
4517 QTest::addColumn<QString>("expectedText");
4518 QTest::addColumn<int>("expectedSelectionStart");
4519 QTest::addColumn<int>("expectedSelectionEnd");
4520 QTest::addColumn<int>("expectedCursorPosition");
4521 QTest::addColumn<bool>("selectionChanged");
4522 QTest::addColumn<bool>("cursorPositionChanged");
4524 QTest::newRow("from cursor position (beginning)")
4529 << standard.at(0).mid(5)
4533 QTest::newRow("to cursor position (beginning)")
4538 << standard.at(0).mid(5)
4542 QTest::newRow("to cursor position (end)")
4545 << standard.at(0).length() << standard.at(0).length()
4546 << standard.at(0).length() << standard.at(0).length() - 5
4547 << standard.at(0).mid(0, standard.at(0).length() - 5)
4548 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4551 QTest::newRow("to cursor position (end)")
4554 << standard.at(0).length() << standard.at(0).length()
4555 << standard.at(0).length() - 5 << standard.at(0).length()
4556 << standard.at(0).mid(0, standard.at(0).length() - 5)
4557 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4560 QTest::newRow("from cursor position (middle)")
4565 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4569 QTest::newRow("to cursor position (middle)")
4574 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4578 QTest::newRow("after cursor position (beginning)")
4583 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4587 QTest::newRow("before cursor position (end)")
4590 << standard.at(0).length() << standard.at(0).length()
4592 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4593 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4596 QTest::newRow("before cursor position (middle)")
4601 << standard.at(0).mid(5)
4605 QTest::newRow("after cursor position (middle)")
4610 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4614 QTest::newRow("before selection")
4619 << standard.at(0).mid(5)
4623 QTest::newRow("before reversed selection")
4628 << standard.at(0).mid(5)
4632 QTest::newRow("after selection")
4636 << standard.at(0).length() - 5 << standard.at(0).length()
4637 << standard.at(0).mid(0, standard.at(0).length() - 5)
4641 QTest::newRow("after reversed selection")
4645 << standard.at(0).length() - 5 << standard.at(0).length()
4646 << standard.at(0).mid(0, standard.at(0).length() - 5)
4650 QTest::newRow("from selection")
4655 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4659 QTest::newRow("from reversed selection")
4664 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4668 QTest::newRow("cropped beginning")
4673 << standard.at(0).mid(4)
4677 QTest::newRow("cropped end")
4681 << 23 << standard.at(0).length() + 8
4682 << standard.at(0).mid(0, 23)
4686 QTest::newRow("cropped beginning and end")
4690 << -9 << standard.at(0).length() + 4
4695 const QString inputMask = "009.009.009.009";
4696 const QString ip = "192.168.2.14";
4698 QTest::newRow("mask: from cursor position")
4703 << QString("192.16..14")
4707 QTest::newRow("mask: to cursor position")
4712 << QString("19.8.2.14")
4716 QTest::newRow("mask: before cursor position")
4721 << QString("2.168.2.14")
4725 QTest::newRow("mask: after cursor position")
4730 << QString("192.168.2.")
4734 QTest::newRow("mask: before selection")
4739 << QString("2.168.2.14")
4743 QTest::newRow("mask: before reversed selection")
4748 << QString("2.168.2.14")
4752 QTest::newRow("mask: after selection")
4757 << QString("192.168.2.")
4761 QTest::newRow("mask: after reversed selection")
4766 << QString("192.168.2.")
4770 QTest::newRow("mask: from selection")
4775 << QString("192.168..14")
4779 QTest::newRow("mask: from reversed selection")
4784 << QString("192.168..14")
4788 QTest::newRow("mask: cropped beginning")
4793 << QString(".168.2.14")
4797 QTest::newRow("mask: cropped end")
4802 << QString("192.168.2.1")
4806 QTest::newRow("mask: cropped beginning and end")
4816 void tst_qquicktextinput::remove()
4818 QFETCH(QString, text);
4819 QFETCH(QString, inputMask);
4820 QFETCH(int, selectionStart);
4821 QFETCH(int, selectionEnd);
4822 QFETCH(int, removeStart);
4823 QFETCH(int, removeEnd);
4824 QFETCH(QString, expectedText);
4825 QFETCH(int, expectedSelectionStart);
4826 QFETCH(int, expectedSelectionEnd);
4827 QFETCH(int, expectedCursorPosition);
4828 QFETCH(bool, selectionChanged);
4829 QFETCH(bool, cursorPositionChanged);
4831 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4832 QQmlComponent textInputComponent(&engine);
4833 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4834 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4835 QVERIFY(textInput != 0);
4837 textInput->select(selectionStart, selectionEnd);
4839 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4840 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4841 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4842 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4843 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4845 textInput->remove(removeStart, removeEnd);
4847 QCOMPARE(textInput->text(), expectedText);
4848 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4850 if (selectionStart > selectionEnd) //
4851 qSwap(selectionStart, selectionEnd);
4853 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4854 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4855 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4857 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4858 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4859 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4860 QCOMPARE(textSpy.count() > 0, text != expectedText);
4862 if (cursorPositionChanged) //
4863 QVERIFY(cursorPositionSpy.count() > 0);
4866 void tst_qquicktextinput::keySequence_data()
4868 QTest::addColumn<QString>("text");
4869 QTest::addColumn<QKeySequence>("sequence");
4870 QTest::addColumn<int>("selectionStart");
4871 QTest::addColumn<int>("selectionEnd");
4872 QTest::addColumn<int>("cursorPosition");
4873 QTest::addColumn<QString>("expectedText");
4874 QTest::addColumn<QString>("selectedText");
4875 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4876 QTest::addColumn<Qt::Key>("layoutDirection");
4878 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4880 QTest::newRow("select all")
4881 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4882 << 44 << standard.at(0) << standard.at(0)
4883 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4884 QTest::newRow("select start of line")
4885 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4886 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4887 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4888 QTest::newRow("select start of block")
4889 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4890 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4891 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4892 QTest::newRow("select end of line")
4893 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4894 << 44 << standard.at(0) << standard.at(0).mid(5)
4895 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4896 QTest::newRow("select end of document") // ### Not handled.
4897 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4898 << 3 << standard.at(0) << QString()
4899 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4900 QTest::newRow("select end of block")
4901 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4902 << 44 << standard.at(0) << standard.at(0).mid(18)
4903 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4904 QTest::newRow("delete end of line")
4905 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4906 << 24 << standard.at(0).mid(0, 24) << QString()
4907 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4908 QTest::newRow("move to start of line")
4909 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4910 << 0 << standard.at(0) << QString()
4911 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4912 QTest::newRow("move to start of block")
4913 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4914 << 0 << standard.at(0) << QString()
4915 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4916 QTest::newRow("move to next char")
4917 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4918 << 13 << standard.at(0) << QString()
4919 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4920 QTest::newRow("move to previous char (ltr)")
4921 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4922 << 2 << standard.at(0) << QString()
4923 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4924 QTest::newRow("move to previous char (rtl)")
4925 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4926 << 4 << standard.at(0) << QString()
4927 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4928 QTest::newRow("move to previous char with selection")
4929 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4930 << 3 << standard.at(0) << QString()
4931 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4932 QTest::newRow("select next char (ltr)")
4933 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4934 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4935 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4936 QTest::newRow("select next char (rtl)")
4937 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4938 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4939 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4940 QTest::newRow("select previous char (ltr)")
4941 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4942 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4943 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4944 QTest::newRow("select previous char (rtl)")
4945 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4946 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4947 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4948 QTest::newRow("move to next word (ltr)")
4949 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4950 << 10 << standard.at(0) << QString()
4951 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4952 QTest::newRow("move to next word (rtl)")
4953 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4954 << 4 << standard.at(0) << QString()
4955 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4956 QTest::newRow("move to next word (password,ltr)")
4957 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4958 << 44 << standard.at(0) << QString()
4959 << QQuickTextInput::Password << Qt::Key_Direction_L;
4960 QTest::newRow("move to next word (password,rtl)")
4961 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4962 << 0 << standard.at(0) << QString()
4963 << QQuickTextInput::Password << Qt::Key_Direction_R;
4964 QTest::newRow("move to previous word (ltr)")
4965 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4966 << 4 << standard.at(0) << QString()
4967 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4968 QTest::newRow("move to previous word (rlt)")
4969 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4970 << 10 << standard.at(0) << QString()
4971 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4972 QTest::newRow("move to previous word (password,ltr)")
4973 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4974 << 0 << standard.at(0) << QString()
4975 << QQuickTextInput::Password << Qt::Key_Direction_L;
4976 QTest::newRow("move to previous word (password,rtl)")
4977 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4978 << 44 << standard.at(0) << QString()
4979 << QQuickTextInput::Password << Qt::Key_Direction_R;
4980 QTest::newRow("select next word")
4981 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4982 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4983 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4984 QTest::newRow("select previous word")
4985 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4986 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4987 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4988 QTest::newRow("delete (selection)")
4989 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4990 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4991 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4992 QTest::newRow("delete (no selection)")
4993 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4994 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4995 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4996 QTest::newRow("delete end of word")
4997 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4998 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4999 << QQuickTextInput::Normal << Qt::Key_Direction_L;
5000 QTest::newRow("delete start of word")
5001 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
5002 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
5003 << QQuickTextInput::Normal << Qt::Key_Direction_L;
5006 void tst_qquicktextinput::keySequence()
5008 QFETCH(QString, text);
5009 QFETCH(QKeySequence, sequence);
5010 QFETCH(int, selectionStart);
5011 QFETCH(int, selectionEnd);
5012 QFETCH(int, cursorPosition);
5013 QFETCH(QString, expectedText);
5014 QFETCH(QString, selectedText);
5015 QFETCH(QQuickTextInput::EchoMode, echoMode);
5016 QFETCH(Qt::Key, layoutDirection);
5018 if (sequence.isEmpty()) {
5019 QSKIP("Key sequence is undefined");
5022 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
5023 QQmlComponent textInputComponent(&engine);
5024 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5025 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5026 QVERIFY(textInput != 0);
5027 textInput->setEchoMode(echoMode);
5029 QQuickWindow window;
5030 textInput->setParentItem(window.rootItem());
5032 window.requestActivateWindow();
5033 QTest::qWaitForWindowActive(&window);
5034 QVERIFY(textInput->hasActiveFocus());
5036 simulateKey(&window, layoutDirection);
5038 textInput->select(selectionStart, selectionEnd);
5040 simulateKeys(&window, sequence);
5042 QCOMPARE(textInput->cursorPosition(), cursorPosition);
5043 QCOMPARE(textInput->text(), expectedText);
5044 QCOMPARE(textInput->selectedText(), selectedText);
5048 #define REPLACE_UNTIL_END 1
5050 void tst_qquicktextinput::undo_data()
5052 QTest::addColumn<QStringList>("insertString");
5053 QTest::addColumn<IntList>("insertIndex");
5054 QTest::addColumn<IntList>("insertMode");
5055 QTest::addColumn<QStringList>("expectedString");
5056 QTest::addColumn<bool>("use_keys");
5058 for (int i=0; i<2; i++) {
5059 QString keys_str = "keyboard";
5060 bool use_keys = true;
5062 keys_str = "insert";
5067 IntList insertIndex;
5069 QStringList insertString;
5070 QStringList expectedString;
5073 insertMode << NORMAL;
5074 insertString << "1";
5077 insertMode << NORMAL;
5078 insertString << "5";
5081 insertMode << NORMAL;
5082 insertString << "3";
5085 insertMode << NORMAL;
5086 insertString << "2";
5089 insertMode << NORMAL;
5090 insertString << "4";
5092 expectedString << "12345";
5093 expectedString << "1235";
5094 expectedString << "135";
5095 expectedString << "15";
5096 expectedString << "";
5098 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
5106 IntList insertIndex;
5108 QStringList insertString;
5109 QStringList expectedString;
5112 insertMode << NORMAL;
5113 insertString << "World"; // World
5116 insertMode << NORMAL;
5117 insertString << "Hello"; // HelloWorld
5120 insertMode << NORMAL;
5121 insertString << "Well"; // WellHelloWorld
5124 insertMode << NORMAL;
5125 insertString << "There"; // WellHelloThereWorld;
5127 expectedString << "WellHelloThereWorld";
5128 expectedString << "WellHelloWorld";
5129 expectedString << "HelloWorld";
5130 expectedString << "World";
5131 expectedString << "";
5133 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
5141 IntList insertIndex;
5143 QStringList insertString;
5144 QStringList expectedString;
5147 insertMode << NORMAL;
5148 insertString << "Ensuring";
5151 insertMode << NORMAL;
5152 insertString << " instan";
5155 insertMode << NORMAL;
5156 insertString << "an ";
5159 insertMode << REPLACE_UNTIL_END;
5160 insertString << " unique instance.";
5162 expectedString << "Ensuring a unique instance.";
5163 expectedString << "Ensuring an instan";
5164 expectedString << "Ensuring instan";
5165 expectedString << "";
5167 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
5177 void tst_qquicktextinput::undo()
5179 QFETCH(QStringList, insertString);
5180 QFETCH(IntList, insertIndex);
5181 QFETCH(IntList, insertMode);
5182 QFETCH(QStringList, expectedString);
5184 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5185 QQmlComponent textInputComponent(&engine);
5186 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5187 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5188 QVERIFY(textInput != 0);
5190 QQuickWindow window;
5191 textInput->setParentItem(window.rootItem());
5193 window.requestActivateWindow();
5194 QTest::qWaitForWindowActive(&window);
5195 QVERIFY(textInput->hasActiveFocus());
5197 QVERIFY(!textInput->canUndo());
5199 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
5203 // STEP 1: First build up an undo history by inserting or typing some strings...
5204 for (i = 0; i < insertString.size(); ++i) {
5205 if (insertIndex[i] > -1)
5206 textInput->setCursorPosition(insertIndex[i]);
5208 // experimental stuff
5209 if (insertMode[i] == REPLACE_UNTIL_END) {
5210 textInput->select(insertIndex[i], insertIndex[i] + 8);
5212 // This is what I actually want...
5213 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
5216 for (int j = 0; j < insertString.at(i).length(); j++)
5217 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5220 QCOMPARE(spy.count(), 1);
5222 // STEP 2: Next call undo several times and see if we can restore to the previous state
5223 for (i = 0; i < expectedString.size() - 1; ++i) {
5224 QCOMPARE(textInput->text(), expectedString[i]);
5225 QVERIFY(textInput->canUndo());
5229 // STEP 3: Verify that we have undone everything
5230 QVERIFY(textInput->text().isEmpty());
5231 QVERIFY(!textInput->canUndo());
5232 QCOMPARE(spy.count(), 2);
5235 void tst_qquicktextinput::redo_data()
5237 QTest::addColumn<QStringList>("insertString");
5238 QTest::addColumn<IntList>("insertIndex");
5239 QTest::addColumn<QStringList>("expectedString");
5242 IntList insertIndex;
5243 QStringList insertString;
5244 QStringList expectedString;
5247 insertString << "World"; // World
5249 insertString << "Hello"; // HelloWorld
5251 insertString << "Well"; // WellHelloWorld
5253 insertString << "There"; // WellHelloThereWorld;
5255 expectedString << "World";
5256 expectedString << "HelloWorld";
5257 expectedString << "WellHelloWorld";
5258 expectedString << "WellHelloThereWorld";
5260 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
5264 void tst_qquicktextinput::redo()
5266 QFETCH(QStringList, insertString);
5267 QFETCH(IntList, insertIndex);
5268 QFETCH(QStringList, expectedString);
5270 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5271 QQmlComponent textInputComponent(&engine);
5272 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5273 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5274 QVERIFY(textInput != 0);
5276 QQuickWindow window;
5277 textInput->setParentItem(window.rootItem());
5279 window.requestActivateWindow();
5280 QTest::qWaitForWindowActive(&window);
5282 QVERIFY(textInput->hasActiveFocus());
5283 QVERIFY(!textInput->canUndo());
5284 QVERIFY(!textInput->canRedo());
5286 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5289 // inserts the diff strings at diff positions
5290 for (i = 0; i < insertString.size(); ++i) {
5291 if (insertIndex[i] > -1)
5292 textInput->setCursorPosition(insertIndex[i]);
5293 for (int j = 0; j < insertString.at(i).length(); j++)
5294 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5295 QVERIFY(textInput->canUndo());
5296 QVERIFY(!textInput->canRedo());
5299 QCOMPARE(spy.count(), 0);
5302 while (!textInput->text().isEmpty()) {
5303 QVERIFY(textInput->canUndo());
5305 QVERIFY(textInput->canRedo());
5308 QCOMPARE(spy.count(), 1);
5310 for (i = 0; i < expectedString.size(); ++i) {
5311 QVERIFY(textInput->canRedo());
5313 QCOMPARE(textInput->text() , expectedString[i]);
5314 QVERIFY(textInput->canUndo());
5316 QVERIFY(!textInput->canRedo());
5317 QCOMPARE(spy.count(), 2);
5320 void tst_qquicktextinput::undo_keypressevents_data()
5322 QTest::addColumn<KeyList>("keys");
5323 QTest::addColumn<QStringList>("expectedString");
5327 QStringList expectedString;
5330 << QKeySequence::MoveToStartOfLine
5337 << QKeySequence::MoveToEndOfLine
5340 expectedString << "BEVERYAFRAID!";
5341 expectedString << "BEVERYAFRAID";
5342 expectedString << "VERYAFRAID";
5343 expectedString << "AFRAID";
5345 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5348 QStringList expectedString;
5351 keys << "1234" << QKeySequence::MoveToStartOfLine
5353 << Qt::Key_Right << Qt::Key_Right
5355 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5359 expectedString << "12";
5360 expectedString << "1234";
5362 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5365 QStringList expectedString;
5369 << QKeySequence::MoveToStartOfLine
5371 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5372 << Qt::Key_Backspace
5373 << QKeySequence::Undo
5375 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5378 expectedString << "AB";
5379 expectedString << "AB12";
5381 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5384 QStringList expectedString;
5389 << Qt::Key_Left << Qt::Key_Left
5393 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5394 // overwriting '1234' with '5'
5396 // undoing deletion of 'AB'
5397 << QKeySequence::Undo
5398 // overwriting '1234' with '6'
5401 expectedString << "ab6cd";
5402 // for versions previous to 3.2 we overwrite needed two undo operations
5403 expectedString << "ab1234cd";
5404 expectedString << "abcd";
5406 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5409 QStringList expectedString;
5414 << Qt::Key_Backspace;
5416 expectedString << "AB";
5417 expectedString << "ABC";
5419 QTest::newRow("Inserts,backspace") << keys << expectedString;
5422 QStringList expectedString;
5426 << Qt::Key_Backspace
5430 expectedString << "ABZ";
5431 expectedString << "AB";
5432 expectedString << "ABC";
5434 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5437 QStringList expectedString;
5440 keys << "123" << QKeySequence::MoveToStartOfLine
5442 << QKeySequence::SelectEndOfLine
5443 // overwriting '123' with 'ABC'
5446 expectedString << "ABC";
5447 expectedString << "123";
5449 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5452 QStringList expectedString;
5456 << QKeySequence::Undo
5457 << QKeySequence::Redo;
5459 expectedString << "123";
5460 expectedString << QString();
5462 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5465 QStringList expectedString;
5467 keys << "hello world"
5468 << (Qt::Key_Backspace | Qt::ControlModifier)
5469 << QKeySequence::Undo
5470 << QKeySequence::Redo
5479 QTest::newRow("Insert,delete previous word,undo,redo,insert") << keys << expectedString;
5482 QStringList expectedString;
5484 keys << "hello world"
5485 << QKeySequence::SelectPreviousWord
5486 << (Qt::Key_Backspace)
5487 << QKeySequence::Undo
5495 QTest::newRow("Insert,select previous word,remove,undo,insert") << keys << expectedString;
5498 QStringList expectedString;
5500 keys << "hello world"
5501 << QKeySequence::DeleteStartOfWord
5502 << QKeySequence::Undo
5506 << "hello worldhello"
5510 QTest::newRow("Insert,delete previous word,undo,insert") << keys << expectedString;
5513 QStringList expectedString;
5515 keys << "hello world"
5516 << QKeySequence::MoveToPreviousWord
5517 << QKeySequence::DeleteEndOfWord
5518 << QKeySequence::Undo
5522 << "hello helloworld"
5526 QTest::newRow("Insert,move,delete next word,undo,insert") << keys << expectedString;
5528 if (!QKeySequence(QKeySequence::DeleteEndOfLine).isEmpty()) { // X11 only.
5530 QStringList expectedString;
5532 keys << "hello world"
5533 << QKeySequence::MoveToStartOfLine
5535 << QKeySequence::DeleteEndOfLine
5536 << QKeySequence::Undo
5540 << "hhelloello world"
5544 QTest::newRow("Insert,move,delete end of line,undo,insert") << keys << expectedString;
5547 QStringList expectedString;
5549 keys << "hello world"
5550 << QKeySequence::MoveToPreviousWord
5551 << (Qt::Key_Left | Qt::ShiftModifier)
5552 << (Qt::Key_Left | Qt::ShiftModifier)
5553 << QKeySequence::DeleteEndOfWord
5554 << QKeySequence::Undo
5562 QTest::newRow("Insert,move,select,delete next word,undo,insert") << keys << expectedString;
5565 #ifndef QT_NO_CLIPBOARD
5567 bool canCopyPaste = true;
5571 PasteboardRef pasteboard;
5572 OSStatus status = PasteboardCreate(0, &pasteboard);
5573 canCopyPaste = status == noErr;
5580 << QKeySequence(QKeySequence::SelectStartOfLine)
5581 << QKeySequence(QKeySequence::Cut)
5583 << QKeySequence(QKeySequence::Paste);
5584 QStringList expectedString = QStringList()
5589 QTest::newRow("Cut,paste") << keys << expectedString;
5594 << QKeySequence(QKeySequence::SelectStartOfLine)
5595 << QKeySequence(QKeySequence::Copy)
5597 << QKeySequence(QKeySequence::Paste);
5598 QStringList expectedString = QStringList()
5603 QTest::newRow("Copy,paste") << keys << expectedString;
5609 void tst_qquicktextinput::undo_keypressevents()
5611 QFETCH(KeyList, keys);
5612 QFETCH(QStringList, expectedString);
5614 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5615 QQmlComponent textInputComponent(&engine);
5616 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5617 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5618 QVERIFY(textInput != 0);
5620 QQuickWindow window;
5621 textInput->setParentItem(window.rootItem());
5623 window.requestActivateWindow();
5624 QTest::qWaitForWindowActive(&window);
5625 QVERIFY(textInput->hasActiveFocus());
5627 simulateKeys(&window, keys);
5629 for (int i = 0; i < expectedString.size(); ++i) {
5630 QCOMPARE(textInput->text() , expectedString[i]);
5633 QVERIFY(textInput->text().isEmpty());
5636 void tst_qquicktextinput::backspaceSurrogatePairs()
5638 // Test backspace, and delete remove both characters in a surrogate pair.
5639 static const quint16 textData[] = { 0xd800, 0xdf00, 0xd800, 0xdf01, 0xd800, 0xdf02, 0xd800, 0xdf03, 0xd800, 0xdf04 };
5640 const QString text = QString::fromUtf16(textData, lengthOf(textData));
5642 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5643 QQmlComponent textInputComponent(&engine);
5644 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5645 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5646 QVERIFY(textInput != 0);
5647 textInput->setText(text);
5648 textInput->setCursorPosition(text.length());
5650 QQuickWindow window;
5651 textInput->setParentItem(window.contentItem());
5653 window.requestActivateWindow();
5654 QVERIFY(QTest::qWaitForWindowActive(&window));
5655 QCOMPARE(QGuiApplication::focusWindow(), &window);
5657 for (int i = text.length(); i >= 0; i -= 2) {
5658 QCOMPARE(textInput->text(), text.mid(0, i));
5659 QTest::keyClick(&window, Qt::Key_Backspace, Qt::NoModifier);
5661 QCOMPARE(textInput->text(), QString());
5663 textInput->setText(text);
5664 textInput->setCursorPosition(0);
5666 for (int i = 0; i < text.length(); i += 2) {
5667 QCOMPARE(textInput->text(), text.mid(i));
5668 QTest::keyClick(&window, Qt::Key_Delete, Qt::NoModifier);
5670 QCOMPARE(textInput->text(), QString());
5673 void tst_qquicktextinput::QTBUG_19956()
5675 QFETCH(QString, url);
5677 QQuickView window(testFileUrl(url));
5679 window.requestActivateWindow();
5680 QTest::qWaitForWindowActive(&window);
5681 QVERIFY(window.rootObject() != 0);
5682 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5684 input->setFocus(true);
5685 QVERIFY(input->hasActiveFocus());
5687 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 30);
5688 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5689 QCOMPARE(window.rootObject()->property("text").toString(), QString("20"));
5690 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5692 window.rootObject()->setProperty("topvalue", 15);
5693 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 15);
5694 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5696 window.rootObject()->setProperty("topvalue", 25);
5697 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 25);
5698 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5700 window.rootObject()->setProperty("bottomvalue", 21);
5701 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 21);
5702 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5704 window.rootObject()->setProperty("bottomvalue", 10);
5705 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5706 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5709 void tst_qquicktextinput::QTBUG_19956_regexp()
5711 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5713 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5714 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5716 QQuickView window(url);
5718 window.requestActivateWindow();
5719 QTest::qWaitForWindowActive(&window);
5720 QVERIFY(window.rootObject() != 0);
5721 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5723 input->setFocus(true);
5724 QVERIFY(input->hasActiveFocus());
5726 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5727 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5728 QCOMPARE(window.rootObject()->property("text").toString(), QString("abc"));
5729 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5731 window.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5732 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5733 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5735 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5736 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5737 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5740 void tst_qquicktextinput::implicitSize_data()
5742 QTest::addColumn<QString>("text");
5743 QTest::addColumn<QString>("wrap");
5744 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5745 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5748 void tst_qquicktextinput::implicitSize()
5750 QFETCH(QString, text);
5751 QFETCH(QString, wrap);
5752 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5753 QQmlComponent textComponent(&engine);
5754 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5755 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5757 QVERIFY(textObject->width() < textObject->implicitWidth());
5758 QVERIFY(textObject->height() == textObject->implicitHeight());
5760 textObject->resetWidth();
5761 QVERIFY(textObject->width() == textObject->implicitWidth());
5762 QVERIFY(textObject->height() == textObject->implicitHeight());
5765 void tst_qquicktextinput::implicitSizeBinding_data()
5767 implicitSize_data();
5770 void tst_qquicktextinput::implicitSizeBinding()
5772 QFETCH(QString, text);
5773 QFETCH(QString, wrap);
5774 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5775 QQmlComponent textComponent(&engine);
5776 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5777 QScopedPointer<QObject> object(textComponent.create());
5778 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5780 QCOMPARE(textObject->width(), textObject->implicitWidth());
5781 QCOMPARE(textObject->height(), textObject->implicitHeight());
5783 textObject->resetWidth();
5784 QCOMPARE(textObject->width(), textObject->implicitWidth());
5785 QCOMPARE(textObject->height(), textObject->implicitHeight());
5787 textObject->resetHeight();
5788 QCOMPARE(textObject->width(), textObject->implicitWidth());
5789 QCOMPARE(textObject->height(), textObject->implicitHeight());
5793 void tst_qquicktextinput::negativeDimensions()
5795 // Verify this doesn't assert during initialization.
5796 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5797 QScopedPointer<QObject> o(component.create());
5799 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5801 QCOMPARE(input->width(), qreal(-1));
5802 QCOMPARE(input->height(), qreal(-1));
5806 void tst_qquicktextinput::setInputMask_data()
5808 QTest::addColumn<QString>("mask");
5809 QTest::addColumn<QString>("input");
5810 QTest::addColumn<QString>("expectedText");
5811 QTest::addColumn<QString>("expectedDisplay");
5812 QTest::addColumn<bool>("insert_text");
5814 // both keyboard and insert()
5815 for (int i=0; i<2; i++) {
5816 bool insert_text = i==0 ? false : true;
5817 QString insert_mode = "keys ";
5819 insert_mode = "insert ";
5821 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5822 << QString("000.000.000.000")
5823 << QString("127.0.0.1")
5824 << QString("127.0.0.1")
5825 << QString("127.0 .0 .1 ")
5826 << bool(insert_text);
5827 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5828 << QString("HH:HH:HH:HH:HH:HH;#")
5829 << QString("00:E0:81:21:9E:8E")
5830 << QString("00:E0:81:21:9E:8E")
5831 << QString("00:E0:81:21:9E:8E")
5832 << bool(insert_text);
5833 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5834 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5835 << QString("AAe081219E8E")
5836 << QString("aa:E0:81:21:9E:8E")
5837 << QString("aa:E0:81:21:9E:8E")
5838 << bool(insert_text);
5839 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5840 << QString("BBBBBBBB;0")
5841 << QString("11011001")
5843 << QString("11011001")
5844 << bool(insert_text);
5845 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5846 << QString("bbbb.bbbb;-")
5847 << QString("110. 0001")
5848 << QString("110.0001")
5849 << QString("110-.0001")
5850 << bool(insert_text);
5851 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5852 << QString("000.000.000.000;0")
5853 << QString("127.0.0.1")
5854 << QString("127...1")
5855 << QString("127.000.000.100")
5856 << bool(insert_text);
5857 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5858 << QString("000.000.000.000")
5859 << QString(".0.0.1")
5860 << QString(".0.0.1")
5861 << QString(" .0 .0 .1 ")
5862 << bool(insert_text);
5863 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5864 << QString("000.000.000.000")
5867 << QString(" . . . ")
5868 << bool(insert_text);
5869 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5870 << QString("000.000.000.000;#")
5873 << QString("###.###.###.###")
5874 << bool(insert_text);
5875 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5876 << QString("000.000.000.000")
5877 << QString("1234123412341234")
5878 << QString("123.412.341.234")
5879 << QString("123.412.341.234")
5880 << bool(insert_text);
5881 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5886 << bool(insert_text);
5887 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5892 << bool(insert_text);
5894 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5899 << bool(insert_text);
5900 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5901 << QString("!A!A!A!A")
5905 << bool(insert_text);
5906 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5911 << bool(insert_text);
5913 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5914 << QString("{n}[0]")
5918 << bool(insert_text);
5919 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5920 << QString("\\\\N\\\\n00")
5924 << bool(insert_text);
5925 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5926 << QString("\\\\\\\\00")
5930 << bool(insert_text);
5931 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5932 << QString("\\\\(00\\\\)")
5936 << bool(insert_text);
5938 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5939 << QString(">AAAA<AAAA!AAAA")
5940 << QString("AbCdEfGhIjKl")
5941 << QString("ABCDefghIjKl")
5942 << QString("ABCDefghIjKl")
5943 << bool(insert_text);
5944 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5945 << QString(">aaaa<aaaa!aaaa")
5946 << QString("AbCdEfGhIjKl")
5947 << QString("ABCDefghIjKl")
5948 << QString("ABCDefghIjKl")
5949 << bool(insert_text);
5951 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5952 << QString(">A<A<A>A>A<A!A!A")
5953 << QString("AbCdEFGH")
5954 << QString("AbcDEfGH")
5955 << QString("AbcDEfGH")
5956 << bool(insert_text);
5957 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5958 << QString(">A<A<A>A>A<A!A!A")
5959 << QString("aBcDefgh")
5960 << QString("AbcDEfgh")
5961 << QString("AbcDEfgh")
5962 << bool(insert_text);
5963 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5964 << QString(">a<a<a>a>a<a!a!a")
5965 << QString("AbCdEFGH")
5966 << QString("AbcDEfGH")
5967 << QString("AbcDEfGH")
5968 << bool(insert_text);
5969 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5970 << QString(">a<a<a>a>a<a!a!a")
5971 << QString("aBcDefgh")
5972 << QString("AbcDEfgh")
5973 << QString("AbcDEfgh")
5974 << bool(insert_text);
5975 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5976 << QString(">H<H<H>H>H<H!H!H")
5977 << QString("aBcDef01")
5978 << QString("AbcDEf01")
5979 << QString("AbcDEf01")
5980 << bool(insert_text);
5981 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5982 << QString(">h<h<h>h>h<h!h!h")
5983 << QString("aBcDef92")
5984 << QString("AbcDEf92")
5985 << QString("AbcDEf92")
5986 << bool(insert_text);
5988 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5989 << QString("AAAAAAAA")
5990 << QString("A2#a;.0!")
5993 << bool(insert_text);
5994 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5996 << QString("f4f4f4f4")
5999 << bool(insert_text);
6000 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
6001 << QString("9999;0")
6005 << bool(insert_text);
6009 void tst_qquicktextinput::setInputMask()
6011 QFETCH(QString, mask);
6012 QFETCH(QString, input);
6013 QFETCH(QString, expectedText);
6014 QFETCH(QString, expectedDisplay);
6015 QFETCH(bool, insert_text);
6017 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6018 QQmlComponent textInputComponent(&engine);
6019 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6020 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6021 QVERIFY(textInput != 0);
6023 // then either insert using insert() or keyboard
6025 textInput->insert(0, input);
6027 QQuickWindow window;
6028 textInput->setParentItem(window.rootItem());
6030 window.requestActivateWindow();
6031 QTest::qWaitForWindowActive(&window);
6032 QVERIFY(textInput->hasActiveFocus());
6034 simulateKey(&window, Qt::Key_Home);
6035 for (int i = 0; i < input.length(); i++)
6036 QTest::keyClick(&window, input.at(i).toLatin1());
6039 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
6040 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
6042 QCOMPARE(textInput->text(), expectedText);
6043 QCOMPARE(textInput->displayText(), expectedDisplay);
6046 void tst_qquicktextinput::inputMask_data()
6048 QTest::addColumn<QString>("mask");
6049 QTest::addColumn<QString>("expectedMask");
6051 // if no mask is set a nul string should be returned
6052 QTest::newRow("nul 1") << QString("") << QString();
6053 QTest::newRow("nul 2") << QString() << QString();
6055 // try different masks
6056 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
6057 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
6058 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
6059 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
6061 // set an invalid input mask...
6062 // the current behaviour is that this exact (faulty) string is returned.
6063 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
6065 // verify that we can unset the mask again
6066 QTest::newRow("unset") << QString("") << QString();
6069 void tst_qquicktextinput::inputMask()
6071 QFETCH(QString, mask);
6072 QFETCH(QString, expectedMask);
6074 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6075 QQmlComponent textInputComponent(&engine);
6076 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6077 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6078 QVERIFY(textInput != 0);
6080 QCOMPARE(textInput->inputMask(), expectedMask);
6083 void tst_qquicktextinput::clearInputMask()
6085 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
6086 QQmlComponent textInputComponent(&engine);
6087 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6088 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6089 QVERIFY(textInput != 0);
6091 QVERIFY(!textInput->inputMask().isEmpty());
6092 textInput->setInputMask(QString());
6093 QCOMPARE(textInput->inputMask(), QString());
6096 void tst_qquicktextinput::keypress_inputMask_data()
6098 QTest::addColumn<QString>("mask");
6099 QTest::addColumn<KeyList>("keys");
6100 QTest::addColumn<QString>("expectedText");
6101 QTest::addColumn<QString>("expectedDisplayText");
6105 // inserting 'A1.2B'
6106 keys << Qt::Key_Home << "A1.2B";
6107 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
6112 keys << Qt::Key_Home << "0!P3";
6113 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
6118 keys << Qt::Key_Home
6120 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6124 // selecting all and delete
6125 keys << Qt::Key_Home
6126 << Key(Qt::ShiftModifier, Qt::Key_End)
6128 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6132 // inserting '12.12' then two backspaces
6133 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
6134 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
6139 keys << Qt::Key_Home << "12ab";
6140 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
6144 void tst_qquicktextinput::keypress_inputMask()
6146 QFETCH(QString, mask);
6147 QFETCH(KeyList, keys);
6148 QFETCH(QString, expectedText);
6149 QFETCH(QString, expectedDisplayText);
6151 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6152 QQmlComponent textInputComponent(&engine);
6153 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6154 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6155 QVERIFY(textInput != 0);
6157 QQuickWindow window;
6158 textInput->setParentItem(window.rootItem());
6160 window.requestActivateWindow();
6161 QTest::qWaitForWindowActive(&window);
6162 QVERIFY(textInput->hasActiveFocus());
6164 simulateKeys(&window, keys);
6166 QCOMPARE(textInput->text(), expectedText);
6167 QCOMPARE(textInput->displayText(), expectedDisplayText);
6171 void tst_qquicktextinput::hasAcceptableInputMask_data()
6173 QTest::addColumn<QString>("optionalMask");
6174 QTest::addColumn<QString>("requiredMask");
6175 QTest::addColumn<QString>("invalid");
6176 QTest::addColumn<QString>("valid");
6178 QTest::newRow("Alphabetic optional and required")
6179 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
6180 QTest::newRow("Alphanumeric optional and require")
6181 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
6182 QTest::newRow("Any optional and required")
6183 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
6184 QTest::newRow("Numeric (0-9) required")
6185 << QString("0000") << QString("9999") << QString("11") << QString("1138");
6186 QTest::newRow("Numeric (1-9) optional and required")
6187 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
6190 void tst_qquicktextinput::hasAcceptableInputMask()
6192 QFETCH(QString, optionalMask);
6193 QFETCH(QString, requiredMask);
6194 QFETCH(QString, invalid);
6195 QFETCH(QString, valid);
6197 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + optionalMask + "\" }";
6198 QQmlComponent textInputComponent(&engine);
6199 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6200 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6201 QVERIFY(textInput != 0);
6203 // test that invalid input (for required) work for optionalMask
6204 textInput->setText(invalid);
6205 QVERIFY(textInput->hasAcceptableInput());
6207 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
6208 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
6210 // test requiredMask
6211 textInput->setInputMask(requiredMask);
6212 textInput->setText(invalid);
6213 QVERIFY(!textInput->hasAcceptableInput());
6215 textInput->setText(valid);
6216 QVERIFY(textInput->hasAcceptableInput());
6219 void tst_qquicktextinput::maskCharacter_data()
6221 QTest::addColumn<QString>("mask");
6222 QTest::addColumn<QString>("input");
6223 QTest::addColumn<bool>("expectedValid");
6225 QTest::newRow("Hex") << QString("H")
6226 << QString("0123456789abcdefABCDEF") << true;
6227 QTest::newRow("hex") << QString("h")
6228 << QString("0123456789abcdefABCDEF") << true;
6229 QTest::newRow("HexInvalid") << QString("H")
6230 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6232 QTest::newRow("hexInvalid") << QString("h")
6233 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6235 QTest::newRow("Bin") << QString("B")
6236 << QString("01") << true;
6237 QTest::newRow("bin") << QString("b")
6238 << QString("01") << true;
6239 QTest::newRow("BinInvalid") << QString("B")
6240 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6242 QTest::newRow("binInvalid") << QString("b")
6243 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6247 void tst_qquicktextinput::maskCharacter()
6249 QFETCH(QString, mask);
6250 QFETCH(QString, input);
6251 QFETCH(bool, expectedValid);
6253 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + mask + "\" }";
6254 QQmlComponent textInputComponent(&engine);
6255 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6256 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6257 QVERIFY(textInput != 0);
6259 for (int i = 0; i < input.size(); ++i) {
6260 QString in = QString(input.at(i));
6261 QString expected = expectedValid ? in : QString();
6262 textInput->setText(QString(input.at(i)));
6263 QCOMPARE(textInput->text(), expected);
6267 QTEST_MAIN(tst_qquicktextinput)
6269 #include "tst_qquicktextinput.moc"