1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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/platformquirks.h"
65 #include "../../shared/platforminputcontext.h"
67 #define SERVER_PORT 14460
68 #define SERVER_ADDR "http://localhost:14460"
70 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
71 Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
72 Q_DECLARE_METATYPE(Qt::Key)
74 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
76 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
78 // XXX This will be replaced by some clever persistent platform image store.
79 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
80 QString arch = "unknown-architecture"; // QTest needs to help with this.
82 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
84 if (!QFile::exists(expectfile)) {
85 actual.save(expectfile);
86 qWarning() << "created" << expectfile;
92 template <typename T> static T evaluate(QObject *scope, const QString &expression)
94 QQmlExpression expr(qmlContext(scope), scope, expression);
95 T result = expr.evaluate().value<T>();
97 qWarning() << expr.error().toString();
101 template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
103 typedef QPair<int, QChar> Key;
105 class tst_qquicktextinput : public QQmlDataTest
110 tst_qquicktextinput();
120 void persistentSelection();
121 void isRightToLeft_data();
122 void isRightToLeft();
123 void moveCursorSelection_data();
124 void moveCursorSelection();
125 void moveCursorSelectionSequence_data();
126 void moveCursorSelectionSequence();
127 void dragMouseSelection();
128 void mouseSelectionMode_data();
129 void mouseSelectionMode();
130 void mouseSelectionMode_accessors();
131 void selectByMouse();
133 void tripleClickSelectsAll();
135 void horizontalAlignment_RightToLeft();
136 void verticalAlignment();
148 void passwordCharacter();
149 void cursorDelegate_data();
150 void cursorDelegate();
151 void remoteCursorDelegate();
152 void cursorVisible();
153 void cursorRectangle_data();
154 void cursorRectangle();
156 void navigation_RTL();
157 #ifndef QT_NO_CLIPBOARD
159 void copyAndPasteKeySequence();
160 void canPasteEmpty();
162 void middleClickPaste();
167 void openInputPanel();
168 void setHAlignClearCache();
169 void focusOutClearSelection();
172 void passwordEchoDelay();
173 void geometrySignals();
176 void preeditAutoScroll();
177 void preeditCursorRectangle();
178 void inputContextMouseHandler();
179 void inputMethodComposing();
180 void inputMethodUpdate();
181 void cursorRectangleSize();
190 void keySequence_data();
197 void undo_keypressevents_data();
198 void undo_keypressevents();
200 void backspaceSurrogatePairs();
203 void QTBUG_19956_data();
204 void QTBUG_19956_regexp();
206 void implicitSize_data();
208 void implicitSizeBinding_data();
209 void implicitSizeBinding();
211 void negativeDimensions();
214 void setInputMask_data();
216 void inputMask_data();
218 void clearInputMask();
219 void keypress_inputMask_data();
220 void keypress_inputMask();
221 void hasAcceptableInputMask_data();
222 void hasAcceptableInputMask();
223 void maskCharacter_data();
224 void maskCharacter();
227 void simulateKey(QWindow *, int key);
229 void simulateKeys(QWindow *window, const QList<Key> &keys);
230 void simulateKeys(QWindow *window, const QKeySequence &sequence);
233 QStringList standard;
234 QStringList colorStrings;
237 typedef QList<int> IntList;
238 Q_DECLARE_METATYPE(IntList)
240 typedef QList<Key> KeyList;
241 Q_DECLARE_METATYPE(KeyList)
243 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
245 for (int i = 0; i < keys.count(); ++i) {
246 const int key = keys.at(i).first & ~Qt::KeyboardModifierMask;
247 const int modifiers = keys.at(i).first & Qt::KeyboardModifierMask;
248 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
250 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
251 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
253 QGuiApplication::sendEvent(window, &press);
254 QGuiApplication::sendEvent(window, &release);
258 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
260 for (int i = 0; i < sequence.count(); ++i) {
261 const int key = sequence[i];
262 const int modifiers = key & Qt::KeyboardModifierMask;
264 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
268 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
270 for (int i = 0; i < sequence.count(); ++i)
271 keys << Key(sequence[i], QChar());
275 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
277 for (int i = 0; i < N - 1; ++i) {
278 int key = QTest::asciiToKey(characters[i]);
279 QChar character = QLatin1Char(characters[i]);
280 keys << Key(key, character);
285 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
287 keys << Key(key, QChar());
291 void tst_qquicktextinput::cleanup()
293 // ensure not even skipped tests with custom input context leave it dangling
294 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
295 inputMethodPrivate->testContext = 0;
298 tst_qquicktextinput::tst_qquicktextinput()
300 standard << "the quick brown fox jumped over the lazy dog"
301 << "It's supercalifragisiticexpialidocious!"
306 colorStrings << "aliceblue"
320 void tst_qquicktextinput::text()
323 QQmlComponent textinputComponent(&engine);
324 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
325 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
327 QVERIFY(textinputObject != 0);
328 QCOMPARE(textinputObject->text(), QString(""));
329 QCOMPARE(textinputObject->length(), 0);
331 delete textinputObject;
334 for (int i = 0; i < standard.size(); i++)
336 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
337 QQmlComponent textinputComponent(&engine);
338 textinputComponent.setData(componentStr.toLatin1(), QUrl());
339 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
341 QVERIFY(textinputObject != 0);
342 QCOMPARE(textinputObject->text(), standard.at(i));
343 QCOMPARE(textinputObject->length(), standard.at(i).length());
345 delete textinputObject;
350 void tst_qquicktextinput::width()
352 // uses Font metrics to find the width for standard
354 QQmlComponent textinputComponent(&engine);
355 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
356 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
358 QVERIFY(textinputObject != 0);
359 QCOMPARE(textinputObject->width(), 0.0);
361 delete textinputObject;
364 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
366 for (int i = 0; i < standard.size(); i++)
368 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
369 QQmlComponent textinputComponent(&engine);
370 textinputComponent.setData(componentStr.toLatin1(), QUrl());
371 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
373 QString s = standard.at(i);
374 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
376 QTextLayout layout(s);
377 layout.setFont(textinputObject->font());
378 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
379 if (requiresUnhintedMetrics) {
381 option.setUseDesignMetrics(true);
382 layout.setTextOption(option);
385 layout.beginLayout();
387 QTextLine line = layout.createLine();
394 qreal metricWidth = ceil(layout.boundingRect().width());
396 QVERIFY(textinputObject != 0);
397 int delta = abs(int(int(textinputObject->width()) - metricWidth));
398 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
400 delete textinputObject;
404 void tst_qquicktextinput::font()
406 //test size, then bold, then italic, then family
408 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
409 QQmlComponent textinputComponent(&engine);
410 textinputComponent.setData(componentStr.toLatin1(), QUrl());
411 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
413 QVERIFY(textinputObject != 0);
414 QCOMPARE(textinputObject->font().pointSize(), 40);
415 QCOMPARE(textinputObject->font().bold(), false);
416 QCOMPARE(textinputObject->font().italic(), false);
418 delete textinputObject;
422 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
423 QQmlComponent textinputComponent(&engine);
424 textinputComponent.setData(componentStr.toLatin1(), QUrl());
425 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
427 QVERIFY(textinputObject != 0);
428 QCOMPARE(textinputObject->font().bold(), true);
429 QCOMPARE(textinputObject->font().italic(), false);
431 delete textinputObject;
435 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
436 QQmlComponent textinputComponent(&engine);
437 textinputComponent.setData(componentStr.toLatin1(), QUrl());
438 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
440 QVERIFY(textinputObject != 0);
441 QCOMPARE(textinputObject->font().italic(), true);
442 QCOMPARE(textinputObject->font().bold(), false);
444 delete textinputObject;
448 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
449 QQmlComponent textinputComponent(&engine);
450 textinputComponent.setData(componentStr.toLatin1(), QUrl());
451 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
453 QVERIFY(textinputObject != 0);
454 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
455 QCOMPARE(textinputObject->font().bold(), false);
456 QCOMPARE(textinputObject->font().italic(), false);
458 delete textinputObject;
462 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
463 QQmlComponent textinputComponent(&engine);
464 textinputComponent.setData(componentStr.toLatin1(), QUrl());
465 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
467 QVERIFY(textinputObject != 0);
468 QCOMPARE(textinputObject->font().family(), QString(""));
470 delete textinputObject;
474 void tst_qquicktextinput::color()
478 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello World\" }";
479 QQmlComponent texteditComponent(&engine);
480 texteditComponent.setData(componentStr.toLatin1(), QUrl());
481 QScopedPointer<QObject> object(texteditComponent.create());
482 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(object.data());
484 QVERIFY(textInputObject);
485 QCOMPARE(textInputObject->color(), QColor("black"));
486 QCOMPARE(textInputObject->selectionColor(), QColor::fromRgba(0xFF000080));
487 QCOMPARE(textInputObject->selectedTextColor(), QColor("white"));
489 QSignalSpy colorSpy(textInputObject, SIGNAL(colorChanged()));
490 QSignalSpy selectionColorSpy(textInputObject, SIGNAL(selectionColorChanged()));
491 QSignalSpy selectedTextColorSpy(textInputObject, SIGNAL(selectedTextColorChanged()));
493 textInputObject->setColor(QColor("white"));
494 QCOMPARE(textInputObject->color(), QColor("white"));
495 QCOMPARE(colorSpy.count(), 1);
497 textInputObject->setSelectionColor(QColor("black"));
498 QCOMPARE(textInputObject->selectionColor(), QColor("black"));
499 QCOMPARE(selectionColorSpy.count(), 1);
501 textInputObject->setSelectedTextColor(QColor("blue"));
502 QCOMPARE(textInputObject->selectedTextColor(), QColor("blue"));
503 QCOMPARE(selectedTextColorSpy.count(), 1);
505 textInputObject->setColor(QColor("white"));
506 QCOMPARE(colorSpy.count(), 1);
508 textInputObject->setSelectionColor(QColor("black"));
509 QCOMPARE(selectionColorSpy.count(), 1);
511 textInputObject->setSelectedTextColor(QColor("blue"));
512 QCOMPARE(selectedTextColorSpy.count(), 1);
514 textInputObject->setColor(QColor("black"));
515 QCOMPARE(textInputObject->color(), QColor("black"));
516 QCOMPARE(colorSpy.count(), 2);
518 textInputObject->setSelectionColor(QColor("blue"));
519 QCOMPARE(textInputObject->selectionColor(), QColor("blue"));
520 QCOMPARE(selectionColorSpy.count(), 2);
522 textInputObject->setSelectedTextColor(QColor("white"));
523 QCOMPARE(textInputObject->selectedTextColor(), QColor("white"));
524 QCOMPARE(selectedTextColorSpy.count(), 2);
528 for (int i = 0; i < colorStrings.size(); i++)
530 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
531 QQmlComponent textinputComponent(&engine);
532 textinputComponent.setData(componentStr.toLatin1(), QUrl());
533 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
534 QVERIFY(textinputObject != 0);
535 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
537 delete textinputObject;
540 //test selection color
541 for (int i = 0; i < colorStrings.size(); i++)
543 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
544 QQmlComponent textinputComponent(&engine);
545 textinputComponent.setData(componentStr.toLatin1(), QUrl());
546 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
547 QVERIFY(textinputObject != 0);
548 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
550 delete textinputObject;
553 //test selected text color
554 for (int i = 0; i < colorStrings.size(); i++)
556 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
557 QQmlComponent textinputComponent(&engine);
558 textinputComponent.setData(componentStr.toLatin1(), QUrl());
559 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
560 QVERIFY(textinputObject != 0);
561 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
563 delete textinputObject;
567 QString colorStr = "#AA001234";
568 QColor testColor("#001234");
569 testColor.setAlpha(170);
571 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
572 QQmlComponent textinputComponent(&engine);
573 textinputComponent.setData(componentStr.toLatin1(), QUrl());
574 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
576 QVERIFY(textinputObject != 0);
577 QCOMPARE(textinputObject->color(), testColor);
579 delete textinputObject;
583 void tst_qquicktextinput::wrap()
586 // for specified width and wrap set true
588 QQmlComponent textComponent(&engine);
589 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
590 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
591 textHeight = textObject->height();
593 QVERIFY(textObject != 0);
594 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
595 QCOMPARE(textObject->width(), 300.);
600 for (int i = 0; i < standard.count(); i++) {
601 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
602 QQmlComponent textComponent(&engine);
603 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
604 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
606 QVERIFY(textObject != 0);
607 QCOMPARE(textObject->width(), 30.);
608 QVERIFY(textObject->height() > textHeight);
610 int oldHeight = textObject->height();
611 textObject->setWidth(100);
612 QVERIFY(textObject->height() < oldHeight);
618 QQmlComponent component(&engine);
619 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
620 QScopedPointer<QObject> object(component.create());
621 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
624 QSignalSpy spy(input, SIGNAL(wrapModeChanged()));
626 QCOMPARE(input->wrapMode(), QQuickTextInput::NoWrap);
628 input->setWrapMode(QQuickTextInput::Wrap);
629 QCOMPARE(input->wrapMode(), QQuickTextInput::Wrap);
630 QCOMPARE(spy.count(), 1);
632 input->setWrapMode(QQuickTextInput::Wrap);
633 QCOMPARE(spy.count(), 1);
635 input->setWrapMode(QQuickTextInput::NoWrap);
636 QCOMPARE(input->wrapMode(), QQuickTextInput::NoWrap);
637 QCOMPARE(spy.count(), 2);
641 void tst_qquicktextinput::selection()
643 QString testStr = standard[0];
644 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
645 QQmlComponent textinputComponent(&engine);
646 textinputComponent.setData(componentStr.toLatin1(), QUrl());
647 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
648 QVERIFY(textinputObject != 0);
651 //Test selection follows cursor
652 for (int i=0; i<= testStr.size(); i++) {
653 textinputObject->setCursorPosition(i);
654 QCOMPARE(textinputObject->cursorPosition(), i);
655 QCOMPARE(textinputObject->selectionStart(), i);
656 QCOMPARE(textinputObject->selectionEnd(), i);
657 QVERIFY(textinputObject->selectedText().isNull());
660 textinputObject->setCursorPosition(0);
661 QVERIFY(textinputObject->cursorPosition() == 0);
662 QVERIFY(textinputObject->selectionStart() == 0);
663 QVERIFY(textinputObject->selectionEnd() == 0);
664 QVERIFY(textinputObject->selectedText().isNull());
666 // Verify invalid positions are ignored.
667 textinputObject->setCursorPosition(-1);
668 QVERIFY(textinputObject->cursorPosition() == 0);
669 QVERIFY(textinputObject->selectionStart() == 0);
670 QVERIFY(textinputObject->selectionEnd() == 0);
671 QVERIFY(textinputObject->selectedText().isNull());
673 textinputObject->setCursorPosition(textinputObject->text().count()+1);
674 QVERIFY(textinputObject->cursorPosition() == 0);
675 QVERIFY(textinputObject->selectionStart() == 0);
676 QVERIFY(textinputObject->selectionEnd() == 0);
677 QVERIFY(textinputObject->selectedText().isNull());
680 for (int i=0; i<= testStr.size(); i++) {
681 textinputObject->select(0,i);
682 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
684 for (int i=0; i<= testStr.size(); i++) {
685 textinputObject->select(i,testStr.size());
686 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
689 textinputObject->setCursorPosition(0);
690 QVERIFY(textinputObject->cursorPosition() == 0);
691 QVERIFY(textinputObject->selectionStart() == 0);
692 QVERIFY(textinputObject->selectionEnd() == 0);
693 QVERIFY(textinputObject->selectedText().isNull());
695 //Test Error Ignoring behaviour
696 textinputObject->setCursorPosition(0);
697 QVERIFY(textinputObject->selectedText().isNull());
698 textinputObject->select(-10,0);
699 QVERIFY(textinputObject->selectedText().isNull());
700 textinputObject->select(100,110);
701 QVERIFY(textinputObject->selectedText().isNull());
702 textinputObject->select(0,-10);
703 QVERIFY(textinputObject->selectedText().isNull());
704 textinputObject->select(0,100);
705 QVERIFY(textinputObject->selectedText().isNull());
706 textinputObject->select(0,10);
707 QVERIFY(textinputObject->selectedText().size() == 10);
708 textinputObject->select(-10,10);
709 QVERIFY(textinputObject->selectedText().size() == 10);
710 textinputObject->select(100,101);
711 QVERIFY(textinputObject->selectedText().size() == 10);
712 textinputObject->select(0,-10);
713 QVERIFY(textinputObject->selectedText().size() == 10);
714 textinputObject->select(0,100);
715 QVERIFY(textinputObject->selectedText().size() == 10);
717 textinputObject->deselect();
718 QVERIFY(textinputObject->selectedText().isNull());
719 textinputObject->select(0,10);
720 QVERIFY(textinputObject->selectedText().size() == 10);
721 textinputObject->deselect();
722 QVERIFY(textinputObject->selectedText().isNull());
724 // test input method selection
725 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
726 textinputObject->setFocus(true);
728 QList<QInputMethodEvent::Attribute> attributes;
729 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
730 QInputMethodEvent event("", attributes);
731 QGuiApplication::sendEvent(textinputObject, &event);
733 QCOMPARE(selectionSpy.count(), 1);
734 QCOMPARE(textinputObject->selectionStart(), 12);
735 QCOMPARE(textinputObject->selectionEnd(), 17);
737 delete textinputObject;
740 void tst_qquicktextinput::persistentSelection()
742 QQuickView window(testFileUrl("persistentSelection.qml"));
744 window.requestActivateWindow();
745 QTest::qWaitForWindowActive(&window);
747 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(window.rootObject());
749 QVERIFY(input->hasActiveFocus());
751 QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
753 QCOMPARE(input->persistentSelection(), false);
755 input->setPersistentSelection(false);
756 QCOMPARE(input->persistentSelection(), false);
757 QCOMPARE(spy.count(), 0);
760 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
762 input->setFocus(false);
763 QCOMPARE(input->property("selected").toString(), QString());
765 input->setFocus(true);
766 QCOMPARE(input->property("selected").toString(), QString());
768 input->setPersistentSelection(true);
769 QCOMPARE(input->persistentSelection(), true);
770 QCOMPARE(spy.count(), 1);
773 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
775 input->setFocus(false);
776 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
778 input->setFocus(true);
779 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
782 void tst_qquicktextinput::isRightToLeft_data()
784 QTest::addColumn<QString>("text");
785 QTest::addColumn<bool>("emptyString");
786 QTest::addColumn<bool>("firstCharacter");
787 QTest::addColumn<bool>("lastCharacter");
788 QTest::addColumn<bool>("middleCharacter");
789 QTest::addColumn<bool>("startString");
790 QTest::addColumn<bool>("midString");
791 QTest::addColumn<bool>("endString");
793 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
794 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
795 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
796 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
797 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
798 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;
799 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
802 void tst_qquicktextinput::isRightToLeft()
804 QFETCH(QString, text);
805 QFETCH(bool, emptyString);
806 QFETCH(bool, firstCharacter);
807 QFETCH(bool, lastCharacter);
808 QFETCH(bool, middleCharacter);
809 QFETCH(bool, startString);
810 QFETCH(bool, midString);
811 QFETCH(bool, endString);
813 QQuickTextInput textInput;
814 textInput.setText(text);
816 // first test that the right string is delivered to the QString::isRightToLeft()
817 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
818 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
819 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
820 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
821 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
822 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
824 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
825 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
827 // then test that the feature actually works
828 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
829 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
830 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
831 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
832 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
833 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
835 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
836 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
839 void tst_qquicktextinput::moveCursorSelection_data()
841 QTest::addColumn<QString>("testStr");
842 QTest::addColumn<int>("cursorPosition");
843 QTest::addColumn<int>("movePosition");
844 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
845 QTest::addColumn<int>("selectionStart");
846 QTest::addColumn<int>("selectionEnd");
847 QTest::addColumn<bool>("reversible");
849 // () contains the text selected by the cursor.
850 // <> contains the actual selection.
852 QTest::newRow("(t)he|characters")
853 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
854 QTest::newRow("do(g)|characters")
855 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
856 QTest::newRow("jum(p)ed|characters")
857 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
858 QTest::newRow("jumped( )over|characters")
859 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
860 QTest::newRow("(the )|characters")
861 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
862 QTest::newRow("( dog)|characters")
863 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
864 QTest::newRow("( jumped )|characters")
865 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
866 QTest::newRow("th(e qu)ick|characters")
867 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
868 QTest::newRow("la(zy d)og|characters")
869 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
870 QTest::newRow("jum(ped ov)er|characters")
871 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
872 QTest::newRow("()the|characters")
873 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
874 QTest::newRow("dog()|characters")
875 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
876 QTest::newRow("jum()ped|characters")
877 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
879 QTest::newRow("<(t)he>|words")
880 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
881 QTest::newRow("<do(g)>|words")
882 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
883 QTest::newRow("<jum(p)ed>|words")
884 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
885 QTest::newRow("<jumped( )>over|words,ltr")
886 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
887 QTest::newRow("jumped<( )over>|words,rtl")
888 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
889 QTest::newRow("<(the )>quick|words,ltr")
890 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
891 QTest::newRow("<(the )quick>|words,rtl")
892 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
893 QTest::newRow("<lazy( dog)>|words,ltr")
894 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
895 QTest::newRow("lazy<( dog)>|words,rtl")
896 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
897 QTest::newRow("<fox( jumped )>over|words,ltr")
898 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
899 QTest::newRow("fox<( jumped )over>|words,rtl")
900 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
901 QTest::newRow("<th(e qu)ick>|words")
902 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
903 QTest::newRow("<la(zy d)og|words>")
904 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
905 QTest::newRow("<jum(ped ov)er>|words")
906 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
907 QTest::newRow("<()>the|words")
908 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
909 QTest::newRow("dog<()>|words")
910 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
911 QTest::newRow("jum<()>ped|words")
912 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
914 QTest::newRow("Hello<(,)> |words")
915 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
916 QTest::newRow("Hello<(, )>world|words,ltr")
917 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
918 QTest::newRow("Hello<(, )world>|words,rtl")
919 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
920 QTest::newRow("<Hel(lo, )>world|words,ltr")
921 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
922 QTest::newRow("<Hel(lo, )world>|words,rtl")
923 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
924 QTest::newRow("<Hel(lo)>,|words")
925 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
926 QTest::newRow("Hello<()>,|words")
927 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
928 QTest::newRow("Hello,<()>|words")
929 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
930 QTest::newRow("Hello<,( )>world|words,ltr")
931 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
932 QTest::newRow("Hello,<( )world>|words,rtl")
933 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
934 QTest::newRow("Hello<,( world)>|words,ltr")
935 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
936 QTest::newRow("Hello,<( world)>|words,rtl")
937 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
938 QTest::newRow("Hello<,( world!)>|words,ltr")
939 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
940 QTest::newRow("Hello,<( world!)>|words,rtl")
941 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
942 QTest::newRow("Hello<(, world!)>|words")
943 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
944 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
946 // QTest::newRow("world<(!)>|words")
947 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
948 QTest::newRow("world!<()>)|words")
949 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
950 QTest::newRow("world<()>!)|words")
951 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
953 QTest::newRow("<(,)>olleH |words")
954 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
955 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
956 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
957 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
958 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
959 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
960 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
961 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
962 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
963 QTest::newRow(",<(ol)leH>,|words")
964 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
965 QTest::newRow(",<()>olleH|words")
966 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
967 QTest::newRow("<()>,olleH|words")
968 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
969 QTest::newRow("<dlrow( )>,olleH|words,ltr")
970 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
971 QTest::newRow("dlrow<( ),>olleH|words,rtl")
972 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
973 QTest::newRow("<(dlrow )>,olleH|words,ltr")
974 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
975 QTest::newRow("<(dlrow ),>olleH|words,rtl")
976 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
977 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
978 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
979 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
980 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
981 QTest::newRow("(!dlrow ,)olleH|words")
982 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
983 QTest::newRow("<(!)>dlrow|words")
984 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
985 QTest::newRow("<()>!dlrow|words")
986 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
987 QTest::newRow("!<()>dlrow|words")
988 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
990 QTest::newRow(" <s(pac)ey> text |words")
991 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
992 QTest::newRow(" spacey <t(ex)t> |words")
993 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
994 QTest::newRow("<( )>spacey text |words|ltr")
995 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
996 QTest::newRow("<( )spacey> text |words|rtl")
997 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
998 QTest::newRow("spacey <text( )>|words|ltr")
999 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
1001 // QTest::newRow("spacey text<( )>|words|rtl")
1002 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
1003 QTest::newRow("<()> spacey text |words")
1004 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
1005 QTest::newRow(" spacey text <()>|words")
1006 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
1009 void tst_qquicktextinput::moveCursorSelection()
1011 QFETCH(QString, testStr);
1012 QFETCH(int, cursorPosition);
1013 QFETCH(int, movePosition);
1014 QFETCH(QQuickTextInput::SelectionMode, mode);
1015 QFETCH(int, selectionStart);
1016 QFETCH(int, selectionEnd);
1017 QFETCH(bool, reversible);
1019 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1020 QQmlComponent textinputComponent(&engine);
1021 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1022 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1023 QVERIFY(textinputObject != 0);
1025 textinputObject->setCursorPosition(cursorPosition);
1026 textinputObject->moveCursorSelection(movePosition, mode);
1028 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1029 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1030 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1033 textinputObject->setCursorPosition(movePosition);
1034 textinputObject->moveCursorSelection(cursorPosition, mode);
1036 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1037 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1038 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1041 delete textinputObject;
1044 void tst_qquicktextinput::moveCursorSelectionSequence_data()
1046 QTest::addColumn<QString>("testStr");
1047 QTest::addColumn<int>("cursorPosition");
1048 QTest::addColumn<int>("movePosition1");
1049 QTest::addColumn<int>("movePosition2");
1050 QTest::addColumn<int>("selection1Start");
1051 QTest::addColumn<int>("selection1End");
1052 QTest::addColumn<int>("selection2Start");
1053 QTest::addColumn<int>("selection2End");
1055 // () contains the text selected by the cursor.
1056 // <> contains the actual selection.
1057 // ^ is the revised cursor position.
1058 // {} contains the revised selection.
1060 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1065 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1070 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1075 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1080 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1085 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1090 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1095 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1100 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1105 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1110 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1115 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1120 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1125 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1130 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1135 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1140 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1145 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1151 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1156 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1162 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1167 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1173 QTest::newRow("{<(^} sp)acey> text |ltr")
1178 QTest::newRow("{<( ^}sp)acey> text |ltr")
1183 QTest::newRow("<( {s^p)acey>} text |rtl")
1188 QTest::newRow("<( {^sp)acey>} text |rtl")
1194 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1199 QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1204 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1209 QTest::newRow(" spacey {<te(xt^} )>|ltr")
1216 void tst_qquicktextinput::moveCursorSelectionSequence()
1218 QFETCH(QString, testStr);
1219 QFETCH(int, cursorPosition);
1220 QFETCH(int, movePosition1);
1221 QFETCH(int, movePosition2);
1222 QFETCH(int, selection1Start);
1223 QFETCH(int, selection1End);
1224 QFETCH(int, selection2Start);
1225 QFETCH(int, selection2End);
1227 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1228 QQmlComponent textinputComponent(&engine);
1229 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1230 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1231 QVERIFY(textinputObject != 0);
1233 textinputObject->setCursorPosition(cursorPosition);
1235 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1236 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1237 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1238 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1240 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1241 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1242 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1243 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1245 delete textinputObject;
1248 void tst_qquicktextinput::dragMouseSelection()
1250 QString qmlfile = testFile("mouseselection_true.qml");
1252 QQuickView window(QUrl::fromLocalFile(qmlfile));
1255 window.requestActivateWindow();
1256 QTest::qWaitForWindowActive(&window);
1258 QVERIFY(window.rootObject() != 0);
1259 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1260 QVERIFY(textInputObject != 0);
1262 // press-and-drag-and-release from x1 to x2
1265 int y = textInputObject->height()/2;
1266 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1267 QTest::mouseMove(&window, QPoint(x2, y));
1268 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1271 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1272 QVERIFY(str1.length() > 3);
1274 // press and drag the current selection.
1277 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1278 QTest::mouseMove(&window, QPoint(x2, y));
1279 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1281 QString str2 = textInputObject->selectedText();
1282 QVERIFY(str2.length() > 3);
1284 QVERIFY(str1 != str2);
1287 void tst_qquicktextinput::mouseSelectionMode_data()
1289 QTest::addColumn<QString>("qmlfile");
1290 QTest::addColumn<bool>("selectWords");
1291 QTest::addColumn<bool>("focus");
1292 QTest::addColumn<bool>("focusOnPress");
1295 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1296 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1297 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1298 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1299 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1300 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1301 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1302 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1303 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1306 void tst_qquicktextinput::mouseSelectionMode()
1308 QFETCH(QString, qmlfile);
1309 QFETCH(bool, selectWords);
1310 QFETCH(bool, focus);
1311 QFETCH(bool, focusOnPress);
1313 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1315 QQuickView window(QUrl::fromLocalFile(qmlfile));
1318 window.requestActivateWindow();
1319 QTest::qWaitForWindowActive(&window);
1321 QVERIFY(window.rootObject() != 0);
1322 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1323 QVERIFY(textInputObject != 0);
1325 textInputObject->setFocus(focus);
1326 textInputObject->setFocusOnPress(focusOnPress);
1328 // press-and-drag-and-release from x1 to x2
1331 int y = textInputObject->height()/2;
1332 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1333 QTest::mouseMove(&window, QPoint(x2,y)); // doesn't work
1334 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1337 QTRY_COMPARE(textInputObject->selectedText(), text);
1339 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1340 QVERIFY(textInputObject->selectedText() != text);
1344 void tst_qquicktextinput::mouseSelectionMode_accessors()
1346 QQmlComponent component(&engine);
1347 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1348 QScopedPointer<QObject> object(component.create());
1349 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1352 QSignalSpy spy(input, SIGNAL(mouseSelectionModeChanged(SelectionMode)));
1354 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1356 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1357 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectWords);
1358 QCOMPARE(spy.count(), 1);
1360 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1361 QCOMPARE(spy.count(), 1);
1363 input->setMouseSelectionMode(QQuickTextInput::SelectCharacters);
1364 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1365 QCOMPARE(spy.count(), 2);
1368 void tst_qquicktextinput::selectByMouse()
1370 QQmlComponent component(&engine);
1371 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1372 QScopedPointer<QObject> object(component.create());
1373 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1376 QSignalSpy spy(input, SIGNAL(selectByMouseChanged(bool)));
1378 QCOMPARE(input->selectByMouse(), false);
1380 input->setSelectByMouse(true);
1381 QCOMPARE(input->selectByMouse(), true);
1382 QCOMPARE(spy.count(), 1);
1383 QCOMPARE(spy.at(0).at(0).toBool(), true);
1385 input->setSelectByMouse(true);
1386 QCOMPARE(spy.count(), 1);
1388 input->setSelectByMouse(false);
1389 QCOMPARE(input->selectByMouse(), false);
1390 QCOMPARE(spy.count(), 2);
1391 QCOMPARE(spy.at(1).at(0).toBool(), false);
1394 void tst_qquicktextinput::renderType()
1396 QQmlComponent component(&engine);
1397 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1398 QScopedPointer<QObject> object(component.create());
1399 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1402 QSignalSpy spy(input, SIGNAL(renderTypeChanged()));
1404 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1406 input->setRenderType(QQuickTextInput::NativeRendering);
1407 QCOMPARE(input->renderType(), QQuickTextInput::NativeRendering);
1408 QCOMPARE(spy.count(), 1);
1410 input->setRenderType(QQuickTextInput::NativeRendering);
1411 QCOMPARE(spy.count(), 1);
1413 input->setRenderType(QQuickTextInput::QtRendering);
1414 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1415 QCOMPARE(spy.count(), 2);
1418 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1420 PlatformInputContext platformInputContext;
1421 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1422 inputMethodPrivate->testContext = &platformInputContext;
1424 QQuickView window(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1425 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1426 QVERIFY(textInput != 0);
1429 const QString rtlText = textInput->text();
1431 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1432 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1434 // implicit alignment should follow the reading direction of RTL text
1435 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1436 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1437 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1438 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1440 // explicitly left aligned
1441 textInput->setHAlign(QQuickTextInput::AlignLeft);
1442 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1443 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1444 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1446 // explicitly right aligned
1447 textInput->setHAlign(QQuickTextInput::AlignRight);
1448 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1449 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1450 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1451 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1453 // explicitly center aligned
1454 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1455 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1456 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1457 QVERIFY(textInput->boundingRect().left() > 0);
1458 QVERIFY(textInput->boundingRect().right() < textInput->width());
1460 // reseted alignment should go back to following the text reading direction
1461 textInput->resetHAlign();
1462 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1463 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1464 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1465 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1467 // mirror the text item
1468 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1470 // mirrored implicit alignment should continue to follow the reading direction of the text
1471 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1472 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1473 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1474 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1476 // explicitly right aligned behaves as left aligned
1477 textInput->setHAlign(QQuickTextInput::AlignRight);
1478 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1479 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1480 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1482 // mirrored explicitly left aligned behaves as right aligned
1483 textInput->setHAlign(QQuickTextInput::AlignLeft);
1484 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1485 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1486 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1487 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1489 // disable mirroring
1490 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1491 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1492 textInput->resetHAlign();
1494 // English text should be implicitly left aligned
1495 textInput->setText("Hello world!");
1496 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1497 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1499 window.requestActivateWindow();
1500 QTest::qWaitForWindowActive(&window);
1501 QVERIFY(textInput->hasActiveFocus());
1503 // If there is no committed text, the preedit text should determine the alignment.
1504 textInput->setText(QString());
1505 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1506 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1507 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1508 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1510 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1511 // redundant as an actual input method may take care of it.
1512 { QInputMethodEvent ev; QGuiApplication::sendEvent(textInput, &ev); }
1514 // empty text with implicit alignment follows the system locale-based
1515 // keyboard input direction from QInputMethod::inputDirection()
1516 textInput->setText("");
1517 platformInputContext.setInputDirection(Qt::LeftToRight);
1518 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1519 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1520 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1522 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1523 platformInputContext.setInputDirection(Qt::RightToLeft);
1524 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1525 QCOMPARE(cursorRectangleSpy.count(), 1);
1526 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1527 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1528 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1530 // set input direction while having content
1531 platformInputContext.setInputDirection(Qt::LeftToRight);
1532 textInput->setText("a");
1533 platformInputContext.setInputDirection(Qt::RightToLeft);
1534 QTest::keyClick(&window, Qt::Key_Backspace);
1535 QVERIFY(textInput->text().isEmpty());
1536 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1537 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1538 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1540 // input direction changed while not having focus
1541 platformInputContext.setInputDirection(Qt::LeftToRight);
1542 textInput->setFocus(false);
1543 platformInputContext.setInputDirection(Qt::RightToLeft);
1544 textInput->setFocus(true);
1545 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1546 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1547 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1549 textInput->setHAlign(QQuickTextInput::AlignRight);
1550 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1551 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1552 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1554 // neutral text should fall back to input direction
1555 textInput->setFocus(true);
1556 textInput->resetHAlign();
1557 textInput->setText(" ()((=<>");
1558 platformInputContext.setInputDirection(Qt::LeftToRight);
1559 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1560 platformInputContext.setInputDirection(Qt::RightToLeft);
1561 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1563 // changing width keeps right aligned cursor on proper position
1564 textInput->setText("");
1565 textInput->setWidth(500);
1566 QVERIFY(textInput->positionToRectangle(0).x() > textInput->width() / 2);
1569 void tst_qquicktextinput::verticalAlignment()
1571 QQuickView window(testFileUrl("horizontalAlignment.qml"));
1572 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1573 QVERIFY(textInput != 0);
1576 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1577 QVERIFY(textInput->boundingRect().bottom() < window.height() / 2);
1578 QVERIFY(textInput->cursorRectangle().bottom() < window.height() / 2);
1579 QVERIFY(textInput->positionToRectangle(0).bottom() < window.height() / 2);
1582 textInput->setVAlign(QQuickTextInput::AlignBottom);
1583 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1584 QVERIFY(textInput->boundingRect().top() > window.height() / 2);
1585 QVERIFY(textInput->cursorRectangle().top() > window.height() / 2);
1586 QVERIFY(textInput->positionToRectangle(0).top() > window.height() / 2);
1588 // explicitly center aligned
1589 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1590 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1591 QVERIFY(textInput->boundingRect().top() < window.height() / 2);
1592 QVERIFY(textInput->boundingRect().bottom() > window.height() / 2);
1593 QVERIFY(textInput->cursorRectangle().top() < window.height() / 2);
1594 QVERIFY(textInput->cursorRectangle().bottom() > window.height() / 2);
1595 QVERIFY(textInput->positionToRectangle(0).top() < window.height() / 2);
1596 QVERIFY(textInput->positionToRectangle(0).bottom() > window.height() / 2);
1599 void tst_qquicktextinput::clipRect()
1601 QQmlComponent component(&engine);
1602 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1603 QScopedPointer<QObject> object(component.create());
1604 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1607 QCOMPARE(input->clipRect().x(), qreal(0));
1608 QCOMPARE(input->clipRect().y(), qreal(0));
1609 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1610 QCOMPARE(input->clipRect().height(), input->height());
1612 input->setText("Hello World");
1613 QCOMPARE(input->clipRect().x(), qreal(0));
1614 QCOMPARE(input->clipRect().y(), qreal(0));
1615 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1616 QCOMPARE(input->clipRect().height(), input->height());
1618 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1619 input->setWidth(input->width() / 2);
1620 QCOMPARE(input->clipRect().x(), qreal(0));
1621 QCOMPARE(input->clipRect().y(), qreal(0));
1622 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1623 QCOMPARE(input->clipRect().height(), input->height());
1625 input->setHeight(input->height() * 2);
1626 QCOMPARE(input->clipRect().x(), qreal(0));
1627 QCOMPARE(input->clipRect().y(), qreal(0));
1628 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1629 QCOMPARE(input->clipRect().height(), input->height());
1631 QQmlComponent cursorComponent(&engine);
1632 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1634 input->setCursorDelegate(&cursorComponent);
1635 input->setCursorVisible(true);
1637 // If a cursor delegate is used it's size should determine the excess width.
1638 QCOMPARE(input->clipRect().x(), qreal(0));
1639 QCOMPARE(input->clipRect().y(), qreal(0));
1640 QCOMPARE(input->clipRect().width(), input->width() + 8);
1641 QCOMPARE(input->clipRect().height(), input->height());
1643 // Alignment, auto scroll, wrapping all don't affect the clip rect.
1644 input->setAutoScroll(false);
1645 QCOMPARE(input->clipRect().x(), qreal(0));
1646 QCOMPARE(input->clipRect().y(), qreal(0));
1647 QCOMPARE(input->clipRect().width(), input->width() + 8);
1648 QCOMPARE(input->clipRect().height(), input->height());
1650 input->setHAlign(QQuickTextInput::AlignRight);
1651 QCOMPARE(input->clipRect().x(), qreal(0));
1652 QCOMPARE(input->clipRect().y(), qreal(0));
1653 QCOMPARE(input->clipRect().width(), input->width() + 8);
1654 QCOMPARE(input->clipRect().height(), input->height());
1656 input->setWrapMode(QQuickTextInput::Wrap);
1657 QCOMPARE(input->clipRect().x(), qreal(0));
1658 QCOMPARE(input->clipRect().y(), qreal(0));
1659 QCOMPARE(input->clipRect().width(), input->width() + 8);
1660 QCOMPARE(input->clipRect().height(), input->height());
1662 input->setVAlign(QQuickTextInput::AlignBottom);
1663 QCOMPARE(input->clipRect().x(), qreal(0));
1664 QCOMPARE(input->clipRect().y(), qreal(0));
1665 QCOMPARE(input->clipRect().width(), input->width() + 8);
1666 QCOMPARE(input->clipRect().height(), input->height());
1669 void tst_qquicktextinput::boundingRect()
1671 QQmlComponent component(&engine);
1672 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1673 QScopedPointer<QObject> object(component.create());
1674 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1678 layout.setFont(input->font());
1680 if (!qmlDisableDistanceField()) {
1682 option.setUseDesignMetrics(true);
1683 layout.setTextOption(option);
1685 layout.beginLayout();
1686 QTextLine line = layout.createLine();
1689 QCOMPARE(input->boundingRect().x(), qreal(0));
1690 QCOMPARE(input->boundingRect().y(), qreal(0));
1691 QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1692 QCOMPARE(input->boundingRect().height(), line.height());
1694 input->setText("Hello World");
1696 layout.setText(input->text());
1697 layout.beginLayout();
1698 line = layout.createLine();
1701 QCOMPARE(input->boundingRect().x(), qreal(0));
1702 QCOMPARE(input->boundingRect().y(), qreal(0));
1703 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1704 QCOMPARE(input->boundingRect().height(), line.height());
1706 // the size of the bounding rect shouldn't be bounded by the size of item.
1707 input->setWidth(input->width() / 2);
1708 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1709 QCOMPARE(input->boundingRect().y(), qreal(0));
1710 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1711 QCOMPARE(input->boundingRect().height(), line.height());
1713 input->setHeight(input->height() * 2);
1714 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1715 QCOMPARE(input->boundingRect().y(), qreal(0));
1716 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1717 QCOMPARE(input->boundingRect().height(), line.height());
1719 QQmlComponent cursorComponent(&engine);
1720 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1722 input->setCursorDelegate(&cursorComponent);
1723 input->setCursorVisible(true);
1725 // Don't include the size of a cursor delegate as it has its own bounding rect.
1726 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1727 QCOMPARE(input->boundingRect().y(), qreal(0));
1728 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1729 QCOMPARE(input->boundingRect().height(), line.height());
1731 // Bounding rect left aligned when auto scroll is disabled;
1732 input->setAutoScroll(false);
1733 QCOMPARE(input->boundingRect().x(), qreal(0));
1734 QCOMPARE(input->boundingRect().y(), qreal(0));
1735 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1736 QCOMPARE(input->boundingRect().height(), line.height());
1738 input->setHAlign(QQuickTextInput::AlignRight);
1739 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1740 QCOMPARE(input->boundingRect().y(), qreal(0));
1741 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1742 QCOMPARE(input->boundingRect().height(), line.height());
1744 input->setWrapMode(QQuickTextInput::Wrap);
1745 QCOMPARE(input->boundingRect().right(), input->width());
1746 QCOMPARE(input->boundingRect().y(), qreal(0));
1747 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1748 QVERIFY(input->boundingRect().height() > line.height());
1750 input->setVAlign(QQuickTextInput::AlignBottom);
1751 QCOMPARE(input->boundingRect().right(), input->width());
1752 QCOMPARE(input->boundingRect().bottom(), input->height());
1753 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1754 QVERIFY(input->boundingRect().height() > line.height());
1757 void tst_qquicktextinput::positionAt()
1759 QQuickView window(testFileUrl("positionAt.qml"));
1760 QVERIFY(window.rootObject() != 0);
1762 window.requestActivateWindow();
1763 QTest::qWaitForWindowActive(&window);
1765 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1766 QVERIFY(textinputObject != 0);
1768 // Check autoscrolled...
1770 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1772 QTextLayout layout(textinputObject->text());
1773 layout.setFont(textinputObject->font());
1775 if (!qmlDisableDistanceField()) {
1777 option.setUseDesignMetrics(true);
1778 layout.setTextOption(option);
1780 layout.beginLayout();
1781 QTextLine line = layout.createLine();
1784 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1785 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1786 int textWidth = floor(line.horizontalAdvance());
1788 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1789 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1791 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1792 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1793 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1795 // Check without autoscroll...
1796 textinputObject->setAutoScroll(false);
1797 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1799 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1800 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1802 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1803 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1805 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1806 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1807 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1809 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1810 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1812 QString preeditText = textinputObject->text().mid(0, pos);
1813 textinputObject->setText(textinputObject->text().mid(pos));
1814 textinputObject->setCursorPosition(0);
1816 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1817 QVERIFY(qGuiApp->focusObject());
1818 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1820 // Check all points within the preedit text return the same position.
1821 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1822 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1823 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1825 // Verify positioning returns to normal after the preedit text.
1826 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1827 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1829 { QInputMethodEvent inputEvent;
1830 QVERIFY(qGuiApp->focusObject());
1831 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1834 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1836 const qreal y0 = line.height() / 2;
1837 const qreal y1 = line.height() * 3 / 2;
1839 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1840 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1842 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1843 QVERIFY(newLinePos > pos);
1844 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1847 void tst_qquicktextinput::maxLength()
1849 QQuickView window(testFileUrl("maxLength.qml"));
1850 QVERIFY(window.rootObject() != 0);
1852 window.requestActivateWindow();
1853 QTest::qWaitForWindowActive(&window);
1855 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1856 QVERIFY(textinputObject != 0);
1857 QVERIFY(textinputObject->text().isEmpty());
1858 QVERIFY(textinputObject->maxLength() == 10);
1859 foreach (const QString &str, standard) {
1860 QVERIFY(textinputObject->text().length() <= 10);
1861 textinputObject->setText(str);
1862 QVERIFY(textinputObject->text().length() <= 10);
1865 textinputObject->setText("");
1866 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1867 for (int i=0; i<20; i++) {
1868 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1869 //simulateKey(&window, Qt::Key_A);
1870 QTest::keyPress(&window, Qt::Key_A);
1871 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1876 void tst_qquicktextinput::masks()
1878 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1879 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1880 QQuickView window(testFileUrl("masks.qml"));
1882 window.requestActivateWindow();
1883 QVERIFY(window.rootObject() != 0);
1884 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1885 QVERIFY(textinputObject != 0);
1886 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1887 QVERIFY(textinputObject->text().length() == 0);
1888 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1889 QCOMPARE(textinputObject->length(), 8);
1890 for (int i=0; i<10; i++) {
1891 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1892 QCOMPARE(textinputObject->length(), 8);
1893 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1894 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1895 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1896 //simulateKey(&window, Qt::Key_A);
1897 QTest::keyPress(&window, Qt::Key_A);
1898 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1903 void tst_qquicktextinput::validators()
1905 // Note that this test assumes that the validators are working properly
1906 // so you may need to run their tests first. All validators are checked
1907 // here to ensure that their exposure to QML is working.
1909 QLocale::setDefault(QLocale(QStringLiteral("C")));
1911 QQuickView window(testFileUrl("validators.qml"));
1913 window.requestActivateWindow();
1914 QTest::qWaitForWindowActive(&window);
1916 QVERIFY(window.rootObject() != 0);
1918 QLocale defaultLocale;
1919 QLocale enLocale("en");
1920 QLocale deLocale("de_DE");
1922 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("intInput")));
1924 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1926 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1927 QVERIFY(intValidator);
1928 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1929 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1930 intValidator->setLocaleName(enLocale.name());
1931 QCOMPARE(intValidator->localeName(), enLocale.name());
1932 QCOMPARE(intInput->validator()->locale(), enLocale);
1933 intValidator->resetLocaleName();
1934 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1935 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1937 intInput->setFocus(true);
1938 QTRY_VERIFY(intInput->hasActiveFocus());
1939 QCOMPARE(intInput->hasAcceptableInput(), false);
1940 QCOMPARE(intInput->property("acceptable").toBool(), false);
1941 QTest::keyPress(&window, Qt::Key_1);
1942 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
1944 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1945 QCOMPARE(intInput->hasAcceptableInput(), false);
1946 QCOMPARE(intInput->property("acceptable").toBool(), false);
1947 QCOMPARE(intSpy.count(), 0);
1948 QTest::keyPress(&window, Qt::Key_2);
1949 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
1951 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1952 QCOMPARE(intInput->hasAcceptableInput(), false);
1953 QCOMPARE(intInput->property("acceptable").toBool(), false);
1954 QCOMPARE(intSpy.count(), 0);
1955 QTest::keyPress(&window, Qt::Key_Period);
1956 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
1958 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1959 QCOMPARE(intInput->hasAcceptableInput(), false);
1960 QTest::keyPress(&window, Qt::Key_Comma);
1961 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
1963 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1964 QCOMPARE(intInput->hasAcceptableInput(), false);
1965 QTest::keyPress(&window, Qt::Key_Backspace);
1966 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
1968 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1969 QCOMPARE(intInput->hasAcceptableInput(), false);
1970 intValidator->setLocaleName(deLocale.name());
1971 QTest::keyPress(&window, Qt::Key_Period);
1972 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
1974 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1975 QCOMPARE(intInput->hasAcceptableInput(), false);
1976 QTest::keyPress(&window, Qt::Key_Backspace);
1977 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
1979 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1980 QCOMPARE(intInput->hasAcceptableInput(), false);
1981 intValidator->resetLocaleName();
1982 QTest::keyPress(&window, Qt::Key_1);
1983 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
1985 QCOMPARE(intInput->text(), QLatin1String("11"));
1986 QCOMPARE(intInput->hasAcceptableInput(), true);
1987 QCOMPARE(intInput->property("acceptable").toBool(), true);
1988 QCOMPARE(intSpy.count(), 1);
1989 QTest::keyPress(&window, Qt::Key_0);
1990 QTest::keyRelease(&window, Qt::Key_0, Qt::NoModifier ,10);
1992 QCOMPARE(intInput->text(), QLatin1String("11"));
1993 QCOMPARE(intInput->hasAcceptableInput(), true);
1994 QCOMPARE(intInput->property("acceptable").toBool(), true);
1995 QCOMPARE(intSpy.count(), 1);
1997 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("dblInput")));
1999 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
2001 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
2002 QVERIFY(dblValidator);
2003 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2004 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2005 dblValidator->setLocaleName(enLocale.name());
2006 QCOMPARE(dblValidator->localeName(), enLocale.name());
2007 QCOMPARE(dblInput->validator()->locale(), enLocale);
2008 dblValidator->resetLocaleName();
2009 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2010 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2012 dblInput->setFocus(true);
2013 QVERIFY(dblInput->hasActiveFocus() == true);
2014 QCOMPARE(dblInput->hasAcceptableInput(), false);
2015 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2016 QTest::keyPress(&window, Qt::Key_1);
2017 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2019 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2020 QCOMPARE(dblInput->hasAcceptableInput(), false);
2021 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2022 QCOMPARE(dblSpy.count(), 0);
2023 QTest::keyPress(&window, Qt::Key_2);
2024 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
2026 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2027 QCOMPARE(dblInput->hasAcceptableInput(), true);
2028 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2029 QCOMPARE(dblSpy.count(), 1);
2030 QTest::keyPress(&window, Qt::Key_Comma);
2031 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
2033 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2034 QCOMPARE(dblInput->hasAcceptableInput(), true);
2035 QTest::keyPress(&window, Qt::Key_1);
2036 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2038 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2039 QCOMPARE(dblInput->hasAcceptableInput(), true);
2040 dblValidator->setLocaleName(deLocale.name());
2041 QCOMPARE(dblInput->hasAcceptableInput(), true);
2042 QTest::keyPress(&window, Qt::Key_1);
2043 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2045 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2046 QCOMPARE(dblInput->hasAcceptableInput(), true);
2047 QTest::keyPress(&window, Qt::Key_1);
2048 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2050 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
2051 QCOMPARE(dblInput->hasAcceptableInput(), true);
2052 QTest::keyPress(&window, Qt::Key_Backspace);
2053 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2055 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2056 QCOMPARE(dblInput->hasAcceptableInput(), true);
2057 QTest::keyPress(&window, Qt::Key_Backspace);
2058 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2060 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2061 QCOMPARE(dblInput->hasAcceptableInput(), true);
2062 QTest::keyPress(&window, Qt::Key_Backspace);
2063 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2065 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2066 QCOMPARE(dblInput->hasAcceptableInput(), true);
2067 dblValidator->resetLocaleName();
2068 QTest::keyPress(&window, Qt::Key_Period);
2069 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
2071 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2072 QCOMPARE(dblInput->hasAcceptableInput(), true);
2073 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2074 QCOMPARE(dblSpy.count(), 1);
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 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2081 QCOMPARE(dblSpy.count(), 1);
2082 QTest::keyPress(&window, Qt::Key_1);
2083 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2085 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2086 QCOMPARE(dblInput->hasAcceptableInput(), true);
2087 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2088 QCOMPARE(dblSpy.count(), 1);
2089 QTest::keyPress(&window, Qt::Key_1);
2090 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2092 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2093 QCOMPARE(dblInput->hasAcceptableInput(), true);
2094 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2095 QCOMPARE(dblSpy.count(), 1);
2097 // Ensure the validator doesn't prevent characters being removed.
2098 dblInput->setValidator(intInput->validator());
2099 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
2100 QCOMPARE(dblInput->hasAcceptableInput(), false);
2101 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2102 QCOMPARE(dblSpy.count(), 2);
2103 QTest::keyPress(&window, Qt::Key_Backspace);
2104 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2106 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2107 QCOMPARE(dblInput->hasAcceptableInput(), false);
2108 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2109 QCOMPARE(dblSpy.count(), 2);
2110 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
2111 QTest::keyPress(&window, Qt::Key_1);
2112 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2114 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2115 QCOMPARE(dblInput->hasAcceptableInput(), false);
2116 QCOMPARE(dblSpy.count(), 2);
2117 QTest::keyPress(&window, Qt::Key_Backspace);
2118 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2120 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2121 QCOMPARE(dblInput->hasAcceptableInput(), false);
2122 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2123 QCOMPARE(dblSpy.count(), 2);
2124 QTest::keyPress(&window, Qt::Key_Backspace);
2125 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2127 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2128 QCOMPARE(dblInput->hasAcceptableInput(), false);
2129 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2130 QCOMPARE(dblSpy.count(), 2);
2131 QTest::keyPress(&window, Qt::Key_Backspace);
2132 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2134 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2135 QCOMPARE(dblInput->hasAcceptableInput(), false);
2136 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2137 QCOMPARE(dblSpy.count(), 2);
2138 QTest::keyPress(&window, Qt::Key_Backspace);
2139 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2141 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2142 QCOMPARE(dblInput->hasAcceptableInput(), false);
2143 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2144 QCOMPARE(dblSpy.count(), 2);
2145 QTest::keyPress(&window, Qt::Key_1);
2146 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2148 QCOMPARE(dblInput->text(), QLatin1String("11"));
2149 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2150 QCOMPARE(dblInput->hasAcceptableInput(), true);
2151 QCOMPARE(dblSpy.count(), 3);
2153 // Changing the validator properties will re-evaluate whether the input is acceptable.
2154 intValidator->setTop(10);
2155 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2156 QCOMPARE(dblInput->hasAcceptableInput(), false);
2157 QCOMPARE(dblSpy.count(), 4);
2158 intValidator->setTop(12);
2159 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2160 QCOMPARE(dblInput->hasAcceptableInput(), true);
2161 QCOMPARE(dblSpy.count(), 5);
2163 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("strInput")));
2165 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2166 strInput->setFocus(true);
2167 QVERIFY(strInput->hasActiveFocus() == true);
2168 QCOMPARE(strInput->hasAcceptableInput(), false);
2169 QCOMPARE(strInput->property("acceptable").toBool(), false);
2170 QTest::keyPress(&window, Qt::Key_1);
2171 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2173 QTRY_COMPARE(strInput->text(), QLatin1String(""));
2174 QCOMPARE(strInput->hasAcceptableInput(), false);
2175 QCOMPARE(strInput->property("acceptable").toBool(), false);
2176 QCOMPARE(strSpy.count(), 0);
2177 QTest::keyPress(&window, Qt::Key_A);
2178 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2180 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2181 QCOMPARE(strInput->hasAcceptableInput(), false);
2182 QCOMPARE(strInput->property("acceptable").toBool(), false);
2183 QCOMPARE(strSpy.count(), 0);
2184 QTest::keyPress(&window, Qt::Key_A);
2185 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2187 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2188 QCOMPARE(strInput->hasAcceptableInput(), true);
2189 QCOMPARE(strInput->property("acceptable").toBool(), true);
2190 QCOMPARE(strSpy.count(), 1);
2191 QTest::keyPress(&window, Qt::Key_A);
2192 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2194 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2195 QCOMPARE(strInput->hasAcceptableInput(), true);
2196 QCOMPARE(strInput->property("acceptable").toBool(), true);
2197 QCOMPARE(strSpy.count(), 1);
2198 QTest::keyPress(&window, Qt::Key_A);
2199 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2201 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2202 QCOMPARE(strInput->hasAcceptableInput(), true);
2203 QCOMPARE(strInput->property("acceptable").toBool(), true);
2204 QCOMPARE(strSpy.count(), 1);
2205 QTest::keyPress(&window, Qt::Key_A);
2206 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2208 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2209 QCOMPARE(strInput->hasAcceptableInput(), true);
2210 QCOMPARE(strInput->property("acceptable").toBool(), true);
2211 QCOMPARE(strSpy.count(), 1);
2213 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("unvalidatedInput")));
2214 QVERIFY(unvalidatedInput);
2215 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2216 unvalidatedInput->setFocus(true);
2217 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2218 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2219 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2220 QTest::keyPress(&window, Qt::Key_1);
2221 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2223 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2224 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2225 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2226 QCOMPARE(unvalidatedSpy.count(), 0);
2227 QTest::keyPress(&window, Qt::Key_A);
2228 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2230 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2231 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2232 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2233 QCOMPARE(unvalidatedSpy.count(), 0);
2236 void tst_qquicktextinput::inputMethods()
2238 QQuickView window(testFileUrl("inputmethods.qml"));
2240 window.requestActivateWindow();
2241 QTest::qWaitForWindowActive(&window);
2243 // test input method hints
2244 QVERIFY(window.rootObject() != 0);
2245 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(window.rootObject());
2246 QVERIFY(input != 0);
2247 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2248 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2249 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2250 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2251 QCOMPARE(inputMethodHintSpy.count(), 1);
2252 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2253 QCOMPARE(inputMethodHintSpy.count(), 1);
2256 QQuickTextInput plainInput;
2257 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2259 input->setFocus(true);
2260 QVERIFY(input->hasActiveFocus() == true);
2261 // test that input method event is committed
2262 QInputMethodEvent event;
2263 event.setCommitString( "My ", -12, 0);
2264 QTRY_COMPARE(qGuiApp->focusObject(), input);
2265 QGuiApplication::sendEvent(input, &event);
2266 QCOMPARE(input->text(), QString("My Hello world!"));
2268 input->setCursorPosition(2);
2269 event.setCommitString("Your", -2, 2);
2270 QGuiApplication::sendEvent(input, &event);
2271 QCOMPARE(input->text(), QString("Your Hello world!"));
2272 QCOMPARE(input->cursorPosition(), 4);
2274 input->setCursorPosition(7);
2275 event.setCommitString("Goodbye", -2, 5);
2276 QGuiApplication::sendEvent(input, &event);
2277 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2278 QCOMPARE(input->cursorPosition(), 12);
2280 input->setCursorPosition(8);
2281 event.setCommitString("Our", -8, 4);
2282 QGuiApplication::sendEvent(input, &event);
2283 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2284 QCOMPARE(input->cursorPosition(), 7);
2286 // input should reset selection even if replacement parameters are out of bounds
2287 input->setText("text");
2288 input->setCursorPosition(0);
2289 input->moveCursorSelection(input->text().length());
2290 event.setCommitString("replacement", -input->text().length(), input->text().length());
2291 QGuiApplication::sendEvent(input, &event);
2292 QCOMPARE(input->selectionStart(), input->selectionEnd());
2294 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2295 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2296 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2298 input->setReadOnly(true);
2299 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2300 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2304 TextInput element should only handle left/right keys until the cursor reaches
2305 the extent of the text, then they should ignore the keys.
2308 void tst_qquicktextinput::navigation()
2310 QQuickView window(testFileUrl("navigation.qml"));
2312 window.requestActivateWindow();
2314 QVERIFY(window.rootObject() != 0);
2316 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2318 QVERIFY(input != 0);
2319 input->setCursorPosition(0);
2320 QTRY_VERIFY(input->hasActiveFocus() == true);
2321 simulateKey(&window, Qt::Key_Left);
2322 QVERIFY(input->hasActiveFocus() == false);
2323 simulateKey(&window, Qt::Key_Right);
2324 QVERIFY(input->hasActiveFocus() == true);
2325 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2326 input->setCursorPosition(input->text().length());
2327 input->select(0,input->text().length());
2328 QVERIFY(input->selectionStart() != input->selectionEnd());
2329 simulateKey(&window, Qt::Key_Right);
2330 QVERIFY(input->selectionStart() == input->selectionEnd());
2331 QVERIFY(input->selectionStart() == input->text().length());
2332 QVERIFY(input->hasActiveFocus() == true);
2333 simulateKey(&window, Qt::Key_Right);
2334 QVERIFY(input->hasActiveFocus() == false);
2335 simulateKey(&window, Qt::Key_Left);
2336 QVERIFY(input->hasActiveFocus() == true);
2338 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2339 input->setCursorPosition(2);
2340 QCOMPARE(input->cursorPosition(),2);
2341 simulateKey(&window, Qt::Key_Up);
2342 QCOMPARE(input->cursorPosition(),2);
2343 simulateKey(&window, Qt::Key_Down);
2344 QCOMPARE(input->cursorPosition(),2);
2346 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2347 input->setText(QString());
2348 QCOMPARE(input->cursorPosition(), 0);
2349 simulateKey(&window, Qt::Key_Right);
2350 QCOMPARE(input->hasActiveFocus(), false);
2351 simulateKey(&window, Qt::Key_Left);
2352 QCOMPARE(input->hasActiveFocus(), true);
2353 simulateKey(&window, Qt::Key_Left);
2354 QCOMPARE(input->hasActiveFocus(), false);
2357 void tst_qquicktextinput::navigation_RTL()
2359 QQuickView window(testFileUrl("navigation.qml"));
2361 window.requestActivateWindow();
2363 QVERIFY(window.rootObject() != 0);
2365 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2367 QVERIFY(input != 0);
2368 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2369 input->setText(QString::fromUtf16(arabic_str, 11));
2371 input->setCursorPosition(0);
2372 QTRY_VERIFY(input->hasActiveFocus() == true);
2375 simulateKey(&window, Qt::Key_Right);
2376 QVERIFY(input->hasActiveFocus() == false);
2379 simulateKey(&window, Qt::Key_Left);
2380 QVERIFY(input->hasActiveFocus() == true);
2382 input->setCursorPosition(input->text().length());
2383 QVERIFY(input->hasActiveFocus() == true);
2386 simulateKey(&window, Qt::Key_Left);
2387 QVERIFY(input->hasActiveFocus() == false);
2390 simulateKey(&window, Qt::Key_Right);
2391 QVERIFY(input->hasActiveFocus() == true);
2394 #ifndef QT_NO_CLIPBOARD
2395 void tst_qquicktextinput::copyAndPaste()
2397 if (!PlatformQuirks::isClipboardAvailable())
2398 QSKIP("This machine doesn't support the clipboard");
2400 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2401 QQmlComponent textInputComponent(&engine);
2402 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2403 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2404 QVERIFY(textInput != 0);
2407 QCOMPARE(textInput->text().length(), 12);
2408 textInput->select(0, textInput->text().length());
2410 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2411 QCOMPARE(textInput->selectedText().length(), 12);
2412 textInput->setCursorPosition(0);
2413 QVERIFY(textInput->canPaste());
2415 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2416 QCOMPARE(textInput->text().length(), 24);
2419 QVERIFY(textInput->canPaste());
2420 textInput->setReadOnly(true);
2421 QVERIFY(!textInput->canPaste());
2423 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2424 QCOMPARE(textInput->text().length(), 24);
2425 textInput->setReadOnly(false);
2426 QVERIFY(textInput->canPaste());
2428 // cut: no selection
2430 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2433 textInput->setCursorPosition(0);
2434 textInput->selectWord();
2435 QCOMPARE(textInput->selectedText(), QString("Hello"));
2438 textInput->setReadOnly(true);
2440 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2441 textInput->setReadOnly(false);
2443 // select all and cut
2444 textInput->selectAll();
2446 QCOMPARE(textInput->text().length(), 0);
2448 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2449 QCOMPARE(textInput->text().length(), 24);
2452 textInput->setCursorPosition(0);
2453 textInput->selectWord();
2455 // copy: no selection, previous copy retained;
2456 textInput->setCursorPosition(0);
2457 QCOMPARE(textInput->selectedText(), QString());
2459 textInput->setText(QString());
2461 QCOMPARE(textInput->text(), QString("Hello"));
2463 // clear copy buffer
2464 QClipboard *clipboard = QGuiApplication::clipboard();
2467 QVERIFY(!textInput->canPaste());
2469 // test that copy functionality is disabled
2470 // when echo mode is set to hide text/password mode
2473 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2474 textInput->setEchoMode(echoMode);
2475 textInput->setText("My password");
2476 textInput->select(0, textInput->text().length());
2478 if (echoMode == QQuickTextInput::Normal) {
2479 QVERIFY(!clipboard->text().isEmpty());
2480 QCOMPARE(clipboard->text(), QString("My password"));
2483 QVERIFY(clipboard->text().isEmpty());
2492 #ifndef QT_NO_CLIPBOARD
2493 void tst_qquicktextinput::copyAndPasteKeySequence()
2495 if (!PlatformQuirks::isClipboardAvailable())
2496 QSKIP("This machine doesn't support the clipboard");
2498 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2499 QQmlComponent textInputComponent(&engine);
2500 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2501 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2502 QVERIFY(textInput != 0);
2504 QQuickWindow window;
2505 textInput->setParentItem(window.rootItem());
2507 window.requestActivateWindow();
2508 QTest::qWaitForWindowActive(&window);
2511 QVERIFY(textInput->hasActiveFocus());
2512 QCOMPARE(textInput->text().length(), 12);
2513 textInput->select(0, textInput->text().length());
2514 simulateKeys(&window, QKeySequence::Copy);
2515 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2516 QCOMPARE(textInput->selectedText().length(), 12);
2517 textInput->setCursorPosition(0);
2518 QVERIFY(textInput->canPaste());
2519 simulateKeys(&window, QKeySequence::Paste);
2520 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2521 QCOMPARE(textInput->text().length(), 24);
2523 // select all and cut
2524 simulateKeys(&window, QKeySequence::SelectAll);
2525 simulateKeys(&window, QKeySequence::Cut);
2526 QCOMPARE(textInput->text().length(), 0);
2527 simulateKeys(&window, QKeySequence::Paste);
2528 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2529 QCOMPARE(textInput->text().length(), 24);
2531 // clear copy buffer
2532 QClipboard *clipboard = QGuiApplication::clipboard();
2535 QVERIFY(!textInput->canPaste());
2537 // test that copy functionality is disabled
2538 // when echo mode is set to hide text/password mode
2541 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2542 textInput->setEchoMode(echoMode);
2543 textInput->setText("My password");
2544 textInput->select(0, textInput->text().length());
2545 simulateKeys(&window, QKeySequence::Copy);
2546 if (echoMode == QQuickTextInput::Normal) {
2547 QVERIFY(!clipboard->text().isEmpty());
2548 QCOMPARE(clipboard->text(), QString("My password"));
2551 QVERIFY(clipboard->text().isEmpty());
2560 #ifndef QT_NO_CLIPBOARD
2561 void tst_qquicktextinput::canPasteEmpty()
2563 QGuiApplication::clipboard()->clear();
2565 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2566 QQmlComponent textInputComponent(&engine);
2567 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2568 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2569 QVERIFY(textInput != 0);
2571 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2572 QCOMPARE(textInput->canPaste(), cp);
2576 #ifndef QT_NO_CLIPBOARD
2577 void tst_qquicktextinput::canPaste()
2579 QGuiApplication::clipboard()->setText("Some text");
2581 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2582 QQmlComponent textInputComponent(&engine);
2583 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2584 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2585 QVERIFY(textInput != 0);
2587 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2588 QCOMPARE(textInput->canPaste(), cp);
2592 #ifndef QT_NO_CLIPBOARD
2593 void tst_qquicktextinput::middleClickPaste()
2595 if (!PlatformQuirks::isClipboardAvailable())
2596 QSKIP("This machine doesn't support the clipboard");
2598 QQuickView window(testFileUrl("mouseselection_true.qml"));
2601 window.requestActivateWindow();
2602 QTest::qWaitForWindowActive(&window);
2604 QVERIFY(window.rootObject() != 0);
2605 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
2606 QVERIFY(textInputObject != 0);
2608 textInputObject->setFocus(true);
2610 QString originalText = textInputObject->text();
2611 QString selectedText = "234567";
2613 // press-and-drag-and-release from x1 to x2
2614 const QPoint p1 = textInputObject->positionToRectangle(2).center().toPoint();
2615 const QPoint p2 = textInputObject->positionToRectangle(8).center().toPoint();
2616 const QPoint p3 = textInputObject->positionToRectangle(1).center().toPoint();
2617 QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
2618 QTest::mouseMove(&window, p2);
2619 QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2);
2620 QTRY_COMPARE(textInputObject->selectedText(), selectedText);
2622 // Middle click pastes the selected text, assuming the platform supports it.
2623 QTest::mouseClick(&window, Qt::MiddleButton, Qt::NoModifier, p3);
2625 // ### This is to prevent double click detection from carrying over to the next test.
2626 QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 10);
2628 if (QGuiApplication::clipboard()->supportsSelection())
2629 QCOMPARE(textInputObject->text().mid(1, selectedText.length()), selectedText);
2631 QCOMPARE(textInputObject->text(), originalText);
2635 void tst_qquicktextinput::passwordCharacter()
2637 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2638 QQmlComponent textInputComponent(&engine);
2639 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2640 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2641 QVERIFY(textInput != 0);
2643 textInput->setPasswordCharacter("X");
2644 qreal implicitWidth = textInput->implicitWidth();
2645 textInput->setPasswordCharacter(".");
2647 // QTBUG-12383 content is updated and redrawn
2648 QVERIFY(textInput->implicitWidth() < implicitWidth);
2653 void tst_qquicktextinput::cursorDelegate_data()
2655 QTest::addColumn<QUrl>("source");
2656 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2657 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2658 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2661 void tst_qquicktextinput::cursorDelegate()
2663 QFETCH(QUrl, source);
2664 QQuickView view(source);
2666 view.requestActivateWindow();
2667 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2668 QVERIFY(textInputObject != 0);
2669 // Delegate is created on demand, and so won't be available immediately. Focus in or
2670 // setCursorVisible(true) will trigger creation.
2671 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2672 QVERIFY(!textInputObject->isCursorVisible());
2673 //Test Delegate gets created
2674 textInputObject->setFocus(true);
2675 QVERIFY(textInputObject->isCursorVisible());
2676 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2677 QVERIFY(delegateObject);
2678 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2679 //Test Delegate gets moved
2680 for (int i=0; i<= textInputObject->text().length(); i++) {
2681 textInputObject->setCursorPosition(i);
2682 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2683 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2685 textInputObject->setCursorPosition(0);
2686 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2687 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2689 // Test delegate gets moved on mouse press.
2690 textInputObject->setSelectByMouse(true);
2691 textInputObject->setCursorPosition(0);
2692 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2693 QTest::qWait(400); //ensure this isn't treated as a double-click
2694 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2696 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2697 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2698 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2700 // Test delegate gets moved on mouse drag
2701 textInputObject->setCursorPosition(0);
2702 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2703 QTest::qWait(400); //ensure this isn't treated as a double-click
2704 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2705 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2706 QGuiApplication::sendEvent(&view, &mv);
2707 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2709 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2710 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2712 textInputObject->setReadOnly(true);
2713 textInputObject->setCursorPosition(0);
2714 QTest::qWait(400); //ensure this isn't treated as a double-click
2715 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2717 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2718 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2719 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2721 textInputObject->setCursorPosition(0);
2722 QTest::qWait(400); //ensure this isn't treated as a double-click
2723 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2725 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2726 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2727 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2729 textInputObject->setCursorPosition(0);
2730 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2731 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2733 textInputObject->setReadOnly(false);
2735 // Delegate moved when text is entered
2736 textInputObject->setText(QString());
2737 for (int i = 0; i < 20; ++i) {
2738 QTest::keyClick(&view, Qt::Key_A);
2739 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2740 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2743 // Delegate moved when text is entered by im.
2744 textInputObject->setText(QString());
2745 for (int i = 0; i < 20; ++i) {
2746 QInputMethodEvent event;
2747 event.setCommitString("w");
2748 QGuiApplication::sendEvent(textInputObject, &event);
2749 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2750 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2752 // Delegate moved when text is removed by im.
2753 for (int i = 19; i > 1; --i) {
2754 QInputMethodEvent event;
2755 event.setCommitString(QString(), -1, 1);
2756 QGuiApplication::sendEvent(textInputObject, &event);
2757 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2758 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2760 { // Delegate moved the text is changed in place by im.
2761 QInputMethodEvent event;
2762 event.setCommitString("i", -1, 1);
2763 QGuiApplication::sendEvent(textInputObject, &event);
2764 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2765 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2768 //Test Delegate gets deleted
2769 textInputObject->setCursorDelegate(0);
2770 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2773 void tst_qquicktextinput::remoteCursorDelegate()
2775 TestHTTPServer server(SERVER_PORT);
2776 server.serveDirectory(dataDirectory());
2780 QQmlComponent component(view.engine(), QUrl(SERVER_ADDR "/RemoteCursor.qml"));
2782 view.rootContext()->setContextProperty("contextDelegate", &component);
2783 view.setSource(testFileUrl("cursorTestRemote.qml"));
2785 view.requestActivateWindow();
2786 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2787 QVERIFY(textInputObject != 0);
2789 // Delegate is created on demand, and so won't be available immediately. Focus in or
2790 // setCursorVisible(true) will trigger creation.
2791 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2792 QVERIFY(!textInputObject->isCursorVisible());
2794 textInputObject->setFocus(true);
2795 QVERIFY(textInputObject->isCursorVisible());
2797 QCOMPARE(component.status(), QQmlComponent::Loading);
2798 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2800 // Wait for component to load.
2801 QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2802 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2805 void tst_qquicktextinput::cursorVisible()
2807 QQuickTextInput input;
2808 input.componentComplete();
2809 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2811 QQuickView view(testFileUrl("cursorVisible.qml"));
2813 view.requestActivateWindow();
2814 QTest::qWaitForWindowActive(&view);
2816 QCOMPARE(input.isCursorVisible(), false);
2818 input.setCursorVisible(true);
2819 QCOMPARE(input.isCursorVisible(), true);
2820 QCOMPARE(spy.count(), 1);
2822 input.setCursorVisible(false);
2823 QCOMPARE(input.isCursorVisible(), false);
2824 QCOMPARE(spy.count(), 2);
2826 input.setFocus(true);
2827 QCOMPARE(input.isCursorVisible(), false);
2828 QCOMPARE(spy.count(), 2);
2830 input.setParentItem(view.rootObject());
2831 QCOMPARE(input.isCursorVisible(), true);
2832 QCOMPARE(spy.count(), 3);
2834 input.setFocus(false);
2835 QCOMPARE(input.isCursorVisible(), false);
2836 QCOMPARE(spy.count(), 4);
2838 input.setFocus(true);
2839 QCOMPARE(input.isCursorVisible(), true);
2840 QCOMPARE(spy.count(), 5);
2842 QQuickView alternateView;
2843 alternateView.show();
2844 alternateView.requestActivateWindow();
2845 QTest::qWaitForWindowActive(&alternateView);
2847 QCOMPARE(input.isCursorVisible(), false);
2848 QCOMPARE(spy.count(), 6);
2850 view.requestActivateWindow();
2851 QTest::qWaitForWindowActive(&view);
2852 QCOMPARE(input.isCursorVisible(), true);
2853 QCOMPARE(spy.count(), 7);
2855 { // Cursor attribute with 0 length hides cursor.
2856 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2857 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2858 QCoreApplication::sendEvent(&input, &ev);
2860 QCOMPARE(input.isCursorVisible(), false);
2861 QCOMPARE(spy.count(), 8);
2863 { // Cursor attribute with non zero length shows cursor.
2864 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2865 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2866 QCoreApplication::sendEvent(&input, &ev);
2868 QCOMPARE(input.isCursorVisible(), true);
2869 QCOMPARE(spy.count(), 9);
2871 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2872 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2873 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2874 QCoreApplication::sendEvent(&input, &ev);
2876 QCOMPARE(input.isCursorVisible(), false);
2877 QCOMPARE(spy.count(), 10);
2879 input.setText("something");
2880 QCOMPARE(input.isCursorVisible(), true);
2881 QCOMPARE(spy.count(), 11);
2883 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2884 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2885 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2886 QCoreApplication::sendEvent(&input, &ev);
2888 QCOMPARE(input.isCursorVisible(), false);
2889 QCOMPARE(spy.count(), 12);
2891 input.setCursorPosition(5);
2892 QCOMPARE(input.isCursorVisible(), true);
2893 QCOMPARE(spy.count(), 13);
2896 void tst_qquicktextinput::cursorRectangle_data()
2898 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2900 QTest::addColumn<QString>("text");
2901 QTest::addColumn<int>("positionAtWidth");
2902 QTest::addColumn<int>("wrapPosition");
2903 QTest::addColumn<QString>("shortText");
2904 QTest::addColumn<bool>("leftToRight");
2906 QTest::newRow("left to right")
2907 << "Hello World!" << 5 << 11
2910 QTest::newRow("right to left")
2911 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2912 << QString::fromUtf16(arabic_str, 3)
2916 void tst_qquicktextinput::cursorRectangle()
2918 QFETCH(QString, text);
2919 QFETCH(int, positionAtWidth);
2920 QFETCH(int, wrapPosition);
2921 QFETCH(QString, shortText);
2922 QFETCH(bool, leftToRight);
2924 QQuickTextInput input;
2925 input.setText(text);
2926 input.componentComplete();
2928 QTextLayout layout(text);
2929 layout.setFont(input.font());
2930 if (!qmlDisableDistanceField()) {
2932 option.setUseDesignMetrics(true);
2933 layout.setTextOption(option);
2935 layout.beginLayout();
2936 QTextLine line = layout.createLine();
2941 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2943 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2944 offset = line.horizontalAdvance() - input.width();
2946 input.setHeight(qCeil(line.height() * 3 / 2));
2950 for (int i = 0; i <= positionAtWidth; ++i) {
2951 input.setCursorPosition(i);
2952 r = input.cursorRectangle();
2954 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2955 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2956 QCOMPARE(input.positionToRectangle(i), r);
2959 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2960 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2962 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2963 input.setCursorPosition(i);
2964 QCOMPARE(r, input.cursorRectangle());
2965 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2966 QCOMPARE(input.positionToRectangle(i), r);
2969 for (int i = text.length() - 2; i >= 0; --i) {
2970 input.setCursorPosition(i);
2971 r = input.cursorRectangle();
2972 QCOMPARE(r.top(), 0.);
2974 QVERIFY(r.right() >= 0);
2976 QVERIFY(r.left() <= input.width());
2978 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2979 QCOMPARE(input.positionToRectangle(i), r);
2982 // Check position with word wrap.
2983 input.setWrapMode(QQuickTextInput::WordWrap);
2984 input.setAutoScroll(false);
2985 for (int i = 0; i < wrapPosition; ++i) {
2986 input.setCursorPosition(i);
2987 r = input.cursorRectangle();
2989 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2990 QCOMPARE(r.top(), 0.);
2991 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2992 QCOMPARE(input.positionToRectangle(i), r);
2995 input.setCursorPosition(wrapPosition);
2996 r = input.cursorRectangle();
2998 QCOMPARE(r.left(), 0.);
3000 QCOMPARE(r.left(), input.width());
3002 QVERIFY(r.top() >= line.height() - 1);
3003 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3004 QCOMPARE(input.positionToRectangle(11), r);
3006 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3007 input.setCursorPosition(i);
3008 r = input.cursorRectangle();
3009 QVERIFY(r.top() >= line.height() - 1);
3010 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3011 QCOMPARE(input.positionToRectangle(i), r);
3014 // Check vertical scrolling with word wrap.
3015 input.setAutoScroll(true);
3016 for (int i = 0; i <= positionAtWidth; ++i) {
3017 input.setCursorPosition(i);
3018 r = input.cursorRectangle();
3020 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
3021 QCOMPARE(r.top(), 0.);
3022 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3023 QCOMPARE(input.positionToRectangle(i), r);
3026 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
3027 // reaches the next non-whitespace character.
3028 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3029 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
3030 input.setCursorPosition(i);
3031 QCOMPARE(r, input.cursorRectangle());
3032 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3033 QCOMPARE(input.positionToRectangle(i), r);
3036 input.setCursorPosition(wrapPosition);
3037 r = input.cursorRectangle();
3039 QCOMPARE(r.left(), 0.);
3041 QCOMPARE(r.left(), input.width());
3043 QVERIFY(r.bottom() >= input.height());
3044 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3045 QCOMPARE(input.positionToRectangle(11), r);
3047 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3048 input.setCursorPosition(i);
3049 r = input.cursorRectangle();
3050 QVERIFY(r.bottom() >= input.height());
3051 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3052 QCOMPARE(input.positionToRectangle(i), r);
3055 for (int i = text.length() - 2; i >= wrapPosition; --i) {
3056 input.setCursorPosition(i);
3057 r = input.cursorRectangle();
3058 QVERIFY(r.bottom() >= input.height());
3059 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3060 QCOMPARE(input.positionToRectangle(i), r);
3063 input.setCursorPosition(wrapPosition - 1);
3064 r = input.cursorRectangle();
3065 QCOMPARE(r.top(), 0.);
3066 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3067 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3068 QCOMPARE(input.positionToRectangle(10), r);
3070 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
3071 input.setCursorPosition(i);
3072 QCOMPARE(r, input.cursorRectangle());
3073 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3074 QCOMPARE(input.positionToRectangle(i), r);
3077 for (int i = positionAtWidth; i >= 0; --i) {
3078 input.setCursorPosition(i);
3079 r = input.cursorRectangle();
3080 QCOMPARE(r.top(), 0.);
3081 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3082 QCOMPARE(input.positionToRectangle(i), r);
3085 input.setText(shortText);
3086 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
3087 r = input.cursorRectangle();
3088 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3091 void tst_qquicktextinput::readOnly()
3093 QQuickView window(testFileUrl("readOnly.qml"));
3095 window.requestActivateWindow();
3097 QVERIFY(window.rootObject() != 0);
3099 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3101 QVERIFY(input != 0);
3102 QTRY_VERIFY(input->hasActiveFocus() == true);
3103 QVERIFY(input->isReadOnly() == true);
3104 QString initial = input->text();
3105 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
3106 simulateKey(&window, k);
3107 simulateKey(&window, Qt::Key_Return);
3108 simulateKey(&window, Qt::Key_Space);
3109 simulateKey(&window, Qt::Key_Escape);
3110 QCOMPARE(input->text(), initial);
3112 input->setCursorPosition(3);
3113 input->setReadOnly(false);
3114 QCOMPARE(input->isReadOnly(), false);
3115 QCOMPARE(input->cursorPosition(), input->text().length());
3118 void tst_qquicktextinput::echoMode()
3120 QQuickView window(testFileUrl("echoMode.qml"));
3122 window.requestActivateWindow();
3123 QTest::qWaitForWindowActive(&window);
3125 QVERIFY(window.rootObject() != 0);
3127 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3129 QVERIFY(input != 0);
3130 QTRY_VERIFY(input->hasActiveFocus() == true);
3131 QString initial = input->text();
3132 Qt::InputMethodHints ref;
3133 QCOMPARE(initial, QLatin1String("ABCDefgh"));
3134 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
3135 QCOMPARE(input->displayText(), input->text());
3137 ref &= ~Qt::ImhHiddenText;
3138 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3139 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3140 input->setEchoMode(QQuickTextInput::NoEcho);
3141 QCOMPARE(input->text(), initial);
3142 QCOMPARE(input->displayText(), QLatin1String(""));
3143 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
3145 ref |= Qt::ImhHiddenText;
3146 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3147 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3148 input->setEchoMode(QQuickTextInput::Password);
3150 ref |= Qt::ImhHiddenText;
3151 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3152 QCOMPARE(input->text(), initial);
3153 QCOMPARE(input->displayText(), QLatin1String("********"));
3154 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3155 // clearing input hints do not clear bits set by echo mode
3156 input->setInputMethodHints(Qt::ImhNone);
3157 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3158 input->setPasswordCharacter(QChar('Q'));
3159 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
3160 QCOMPARE(input->text(), initial);
3161 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3162 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
3163 //PasswordEchoOnEdit
3164 ref &= ~Qt::ImhHiddenText;
3165 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3166 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3167 QCOMPARE(input->text(), initial);
3168 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3169 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
3170 QTest::keyPress(&window, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
3171 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
3172 QCOMPARE(input->text(), QLatin1String("a"));
3173 QCOMPARE(input->displayText(), QLatin1String("a"));
3174 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
3175 input->setFocus(false);
3176 QVERIFY(input->hasActiveFocus() == false);
3177 QCOMPARE(input->displayText(), QLatin1String("Q"));
3178 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
3179 input->setFocus(true);
3180 QVERIFY(input->hasActiveFocus());
3181 QInputMethodEvent inputEvent;
3182 inputEvent.setCommitString(initial);
3183 QGuiApplication::sendEvent(input, &inputEvent);
3184 QCOMPARE(input->text(), initial);
3185 QCOMPARE(input->displayText(), initial);
3186 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
3189 void tst_qquicktextinput::passwordEchoDelay()
3191 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
3193 QSKIP("No mask delay in use");
3194 QQuickView window(testFileUrl("echoMode.qml"));
3196 window.requestActivateWindow();
3197 QTest::qWaitForWindowActive(&window);
3199 QVERIFY(window.rootObject() != 0);
3201 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3203 QVERIFY(input->hasActiveFocus());
3205 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3208 QChar fillChar = QLatin1Char('*');
3210 input->setEchoMode(QQuickTextInput::Password);
3211 QCOMPARE(input->displayText(), QString(8, fillChar));
3212 input->setText(QString());
3213 QCOMPARE(input->displayText(), QString());
3215 QTest::keyPress(&window, '0');
3216 QTest::keyPress(&window, '1');
3217 QTest::keyPress(&window, '2');
3218 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
3219 QTest::keyPress(&window, '3');
3220 QTest::keyPress(&window, '4');
3221 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3222 QTest::keyPress(&window, Qt::Key_Backspace);
3223 QCOMPARE(input->displayText(), QString(4, fillChar));
3224 QTest::keyPress(&window, '4');
3225 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3226 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3228 // Verify the last character entered is replaced by the fill character after a delay.
3229 // Also check the cursor position is updated to accomdate a size difference between
3230 // the fill character and the replaced character.
3231 QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged()));
3232 QTest::qWait(maskDelay);
3233 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
3234 QCOMPARE(cursorSpy.count(), 1);
3235 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3237 QTest::keyPress(&window, '5');
3238 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3239 input->setFocus(false);
3240 QVERIFY(!input->hasFocus());
3241 QCOMPARE(input->displayText(), QString(6, fillChar));
3242 input->setFocus(true);
3243 QTRY_VERIFY(input->hasFocus());
3244 QCOMPARE(input->displayText(), QString(6, fillChar));
3245 QTest::keyPress(&window, '6');
3246 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3248 QInputMethodEvent ev;
3249 ev.setCommitString(QLatin1String("7"));
3250 QGuiApplication::sendEvent(input, &ev);
3251 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3253 input->setCursorPosition(3);
3254 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3255 QTest::keyPress(&window, 'a');
3256 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3257 QTest::keyPress(&window, Qt::Key_Backspace);
3258 QCOMPARE(input->displayText(), QString(8, fillChar));
3262 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3264 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3265 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3267 QGuiApplication::sendEvent(view, &press);
3268 QGuiApplication::sendEvent(view, &release);
3272 void tst_qquicktextinput::focusOnPress()
3274 QString componentStr =
3275 "import QtQuick 2.0\n"
3277 "property bool selectOnFocus: false\n"
3278 "width: 100; height: 50\n"
3279 "activeFocusOnPress: true\n"
3280 "text: \"Hello World\"\n"
3281 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3283 QQmlComponent texteditComponent(&engine);
3284 texteditComponent.setData(componentStr.toLatin1(), QUrl());
3285 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3286 QVERIFY(textInputObject != 0);
3287 QCOMPARE(textInputObject->focusOnPress(), true);
3288 QCOMPARE(textInputObject->hasFocus(), false);
3290 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3291 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3292 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3294 textInputObject->setFocusOnPress(true);
3295 QCOMPARE(textInputObject->focusOnPress(), true);
3296 QCOMPARE(activeFocusOnPressSpy.count(), 0);
3298 QQuickWindow window;
3299 window.resize(100, 50);
3300 textInputObject->setParentItem(window.rootItem());
3302 window.requestActivateWindow();
3303 QTest::qWaitForWindowActive(&window);
3305 QCOMPARE(textInputObject->hasFocus(), false);
3306 QCOMPARE(textInputObject->hasActiveFocus(), false);
3308 QPoint centerPoint(window.width()/2, window.height()/2);
3309 Qt::KeyboardModifiers noModifiers = 0;
3310 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3311 QGuiApplication::processEvents();
3312 QCOMPARE(textInputObject->hasFocus(), true);
3313 QCOMPARE(textInputObject->hasActiveFocus(), true);
3314 QCOMPARE(focusSpy.count(), 1);
3315 QCOMPARE(activeFocusSpy.count(), 1);
3316 QCOMPARE(textInputObject->selectedText(), QString());
3317 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3319 textInputObject->setFocusOnPress(false);
3320 QCOMPARE(textInputObject->focusOnPress(), false);
3321 QCOMPARE(activeFocusOnPressSpy.count(), 1);
3323 textInputObject->setFocus(false);
3324 QCOMPARE(textInputObject->hasFocus(), false);
3325 QCOMPARE(textInputObject->hasActiveFocus(), false);
3326 QCOMPARE(focusSpy.count(), 2);
3327 QCOMPARE(activeFocusSpy.count(), 2);
3329 // Wait for double click timeout to expire before clicking again.
3331 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3332 QGuiApplication::processEvents();
3333 QCOMPARE(textInputObject->hasFocus(), false);
3334 QCOMPARE(textInputObject->hasActiveFocus(), false);
3335 QCOMPARE(focusSpy.count(), 2);
3336 QCOMPARE(activeFocusSpy.count(), 2);
3337 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3339 textInputObject->setFocusOnPress(true);
3340 QCOMPARE(textInputObject->focusOnPress(), true);
3341 QCOMPARE(activeFocusOnPressSpy.count(), 2);
3343 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3344 textInputObject->setProperty("selectOnFocus", true);
3347 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3348 QGuiApplication::processEvents();
3349 QCOMPARE(textInputObject->hasFocus(), true);
3350 QCOMPARE(textInputObject->hasActiveFocus(), true);
3351 QCOMPARE(focusSpy.count(), 3);
3352 QCOMPARE(activeFocusSpy.count(), 3);
3353 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3354 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3357 void tst_qquicktextinput::openInputPanel()
3359 PlatformInputContext platformInputContext;
3360 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3361 inputMethodPrivate->testContext = &platformInputContext;
3363 QQuickView view(testFileUrl("openInputPanel.qml"));
3365 view.requestActivateWindow();
3366 QTest::qWaitForWindowActive(&view);
3368 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3371 // check default values
3372 QVERIFY(input->focusOnPress());
3373 QVERIFY(!input->hasActiveFocus());
3374 QVERIFY(qApp->focusObject() != input);
3375 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3377 // input panel should open on focus
3378 QPoint centerPoint(view.width()/2, view.height()/2);
3379 Qt::KeyboardModifiers noModifiers = 0;
3380 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3381 QGuiApplication::processEvents();
3382 QVERIFY(input->hasActiveFocus());
3383 QCOMPARE(qApp->focusObject(), input);
3384 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3385 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3387 // input panel should be re-opened when pressing already focused TextInput
3388 qApp->inputMethod()->hide();
3389 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3390 QVERIFY(input->hasActiveFocus());
3391 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3392 QGuiApplication::processEvents();
3393 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3394 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3396 // input panel should stay visible if focus is lost to another text inputor
3397 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3398 QQuickTextInput anotherInput;
3399 anotherInput.componentComplete();
3400 anotherInput.setParentItem(view.rootObject());
3401 anotherInput.setFocus(true);
3402 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3403 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3404 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3406 anotherInput.setFocus(false);
3407 QVERIFY(qApp->focusObject() != &anotherInput);
3408 QCOMPARE(view.activeFocusItem(), view.rootItem());
3409 anotherInput.setFocus(true);
3411 qApp->inputMethod()->hide();
3413 // input panel should not be opened if TextInput is read only
3414 input->setReadOnly(true);
3415 input->setFocus(true);
3416 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3417 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3418 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3419 QGuiApplication::processEvents();
3420 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3422 // input panel should not be opened if focusOnPress is set to false
3423 input->setFocusOnPress(false);
3424 input->setFocus(false);
3425 input->setFocus(true);
3426 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3427 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3428 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3429 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3432 class MyTextInput : public QQuickTextInput
3435 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3439 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3442 return QQuickTextInput::updatePaintNode(node, data);
3447 void tst_qquicktextinput::setHAlignClearCache()
3451 input.setText("Hello world");
3452 input.setParentItem(view.rootItem());
3454 view.requestActivateWindow();
3455 QTest::qWaitForWindowActive(&view);
3457 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3459 QTRY_COMPARE(input.nbPaint, 1);
3460 input.setHAlign(QQuickTextInput::AlignRight);
3461 //Changing the alignment should trigger a repaint
3462 QTRY_COMPARE(input.nbPaint, 2);
3465 void tst_qquicktextinput::focusOutClearSelection()
3468 QQuickTextInput input;
3469 QQuickTextInput input2;
3470 input.setText(QLatin1String("Hello world"));
3471 input.setFocus(true);
3472 input2.setParentItem(view.rootItem());
3473 input.setParentItem(view.rootItem());
3474 input.componentComplete();
3475 input2.componentComplete();
3477 view.requestActivateWindow();
3478 QTest::qWaitForWindowActive(&view);
3479 QVERIFY(input.hasActiveFocus());
3481 //The selection should work
3482 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3483 input2.setFocus(true);
3484 QGuiApplication::processEvents();
3485 //The input lost the focus selection should be cleared
3486 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3489 void tst_qquicktextinput::geometrySignals()
3491 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3492 QObject *o = component.create();
3494 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3495 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3499 void tst_qquicktextinput::contentSize()
3501 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3502 QQmlComponent textComponent(&engine);
3503 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3504 QScopedPointer<QObject> object(textComponent.create());
3505 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3507 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3509 textObject->setText("The quick red fox jumped over the lazy brown dog");
3511 QVERIFY(textObject->contentWidth() > textObject->width());
3512 QVERIFY(textObject->contentHeight() < textObject->height());
3513 QCOMPARE(spy.count(), 1);
3515 textObject->setWrapMode(QQuickTextInput::WordWrap);
3516 QVERIFY(textObject->contentWidth() <= textObject->width());
3517 QVERIFY(textObject->contentHeight() > textObject->height());
3518 QCOMPARE(spy.count(), 2);
3520 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3522 QVERIFY(textObject->contentWidth() > textObject->width());
3523 QVERIFY(textObject->contentHeight() > textObject->height());
3524 QCOMPARE(spy.count(), 3);
3526 textObject->setText("The quick red fox jumped over the lazy brown dog");
3527 for (int w = 60; w < 120; ++w) {
3528 textObject->setWidth(w);
3529 QVERIFY(textObject->contentWidth() <= textObject->width());
3530 QVERIFY(textObject->contentHeight() > textObject->height());
3534 static void sendPreeditText(QQuickItem *item, const QString &text, int cursor)
3536 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3537 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3538 QCoreApplication::sendEvent(item, &event);
3541 void tst_qquicktextinput::preeditAutoScroll()
3543 QString preeditText = "califragisiticexpialidocious!";
3545 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3547 view.requestActivateWindow();
3548 QTest::qWaitForWindowActive(&view);
3549 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3551 QVERIFY(input->hasActiveFocus());
3553 input->setWidth(input->implicitWidth());
3555 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3556 int cursorRectangleChanges = 0;
3558 // test the text is scrolled so the preedit is visible.
3559 sendPreeditText(input, preeditText.mid(0, 3), 1);
3560 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3561 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3562 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3564 // test the text is scrolled back when the preedit is removed.
3565 QInputMethodEvent imEvent;
3566 QCoreApplication::sendEvent(input, &imEvent);
3567 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3568 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3569 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3571 QTextLayout layout(preeditText);
3572 layout.setFont(input->font());
3573 if (!qmlDisableDistanceField()) {
3575 option.setUseDesignMetrics(true);
3576 layout.setTextOption(option);
3578 layout.beginLayout();
3579 QTextLine line = layout.createLine();
3582 // test if the preedit is larger than the text input that the
3583 // character preceding the cursor is still visible.
3584 qreal x = input->positionToRectangle(0).x();
3585 for (int i = 0; i < 3; ++i) {
3586 sendPreeditText(input, preeditText, i + 1);
3587 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3588 QVERIFY(input->cursorRectangle().right() >= width - 3);
3589 QVERIFY(input->positionToRectangle(0).x() < x);
3590 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3591 x = input->positionToRectangle(0).x();
3593 for (int i = 1; i >= 0; --i) {
3594 sendPreeditText(input, preeditText, i + 1);
3595 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3596 QVERIFY(input->cursorRectangle().right() >= width - 3);
3597 QVERIFY(input->positionToRectangle(0).x() > x);
3598 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3599 x = input->positionToRectangle(0).x();
3602 // Test incrementing the preedit cursor doesn't cause further
3603 // scrolling when right most text is visible.
3604 sendPreeditText(input, preeditText, preeditText.length() - 3);
3605 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3606 x = input->positionToRectangle(0).x();
3607 for (int i = 2; i >= 0; --i) {
3608 sendPreeditText(input, preeditText, preeditText.length() - i);
3609 QCOMPARE(input->positionToRectangle(0).x(), x);
3610 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3612 for (int i = 1; i < 3; ++i) {
3613 sendPreeditText(input, preeditText, preeditText.length() - i);
3614 QCOMPARE(input->positionToRectangle(0).x(), x);
3615 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3618 // Test disabling auto scroll.
3619 QCoreApplication::sendEvent(input, &imEvent);
3621 input->setAutoScroll(false);
3622 sendPreeditText(input, preeditText.mid(0, 3), 1);
3623 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3624 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3627 void tst_qquicktextinput::preeditCursorRectangle()
3629 QString preeditText = "super";
3631 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3633 view.requestActivateWindow();
3634 QTest::qWaitForWindowActive(&view);
3635 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3637 QVERIFY(input->hasActiveFocus());
3639 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3644 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3645 QCoreApplication::sendEvent(input, &query);
3646 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3648 // Verify that the micro focus rect is positioned the same for position 0 as
3649 // it would be if there was no preedit text.
3650 sendPreeditText(input, preeditText, 0);
3651 QCoreApplication::sendEvent(input, &query);
3652 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3653 QCOMPARE(currentRect, previousRect);
3654 QCOMPARE(input->cursorRectangle(), currentRect);
3655 QCOMPARE(cursor->pos(), currentRect.topLeft());
3657 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3658 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3660 // Verify that the micro focus rect moves to the left as the cursor position
3662 for (int i = 1; i <= 5; ++i) {
3663 sendPreeditText(input, preeditText, i);
3664 QCoreApplication::sendEvent(input, &query);
3665 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3666 QVERIFY(previousRect.left() < currentRect.left());
3667 QCOMPARE(input->cursorRectangle(), currentRect);
3668 QCOMPARE(cursor->pos(), currentRect.topLeft());
3669 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3670 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3671 previousRect = currentRect;
3674 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3675 // but the (non-zero) cursor position is the same.
3678 sendPreeditText(input, "wwwww", 5);
3679 QCoreApplication::sendEvent(input, &query);
3680 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3681 QCOMPARE(input->cursorRectangle(), currentRect);
3682 QCOMPARE(cursor->pos(), currentRect.topLeft());
3683 QCOMPARE(inputSpy.count(), 1);
3684 QCOMPARE(panelSpy.count(), 1);
3686 // Verify that if there is no preedit cursor then the micro focus rect is the
3687 // same as it would be if it were positioned at the end of the preedit text.
3690 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3691 QCoreApplication::sendEvent(input, &imEvent); }
3692 QCoreApplication::sendEvent(input, &query);
3693 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3694 QCOMPARE(currentRect, previousRect);
3695 QCOMPARE(input->cursorRectangle(), currentRect);
3696 QCOMPARE(cursor->pos(), currentRect.topLeft());
3697 QCOMPARE(inputSpy.count(), 1);
3698 QCOMPARE(panelSpy.count(), 1);
3701 void tst_qquicktextinput::inputContextMouseHandler()
3703 PlatformInputContext platformInputContext;
3704 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3705 inputMethodPrivate->testContext = &platformInputContext;
3707 QString text = "supercalifragisiticexpialidocious!";
3708 QQuickView view(testFileUrl("inputContext.qml"));
3709 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3712 input->setFocus(true);
3716 view.requestActivateWindow();
3717 QTest::qWaitForWindowActive(&view);
3719 QTextLayout layout(text);
3720 layout.setFont(input->font());
3721 if (!qmlDisableDistanceField()) {
3723 option.setUseDesignMetrics(true);
3724 layout.setTextOption(option);
3726 layout.beginLayout();
3727 QTextLine line = layout.createLine();
3730 const qreal x = line.cursorToX(2, QTextLine::Leading);
3731 const qreal y = line.height() / 2;
3732 QPoint position = QPointF(x, y).toPoint();
3734 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3735 QGuiApplication::sendEvent(input, &inputEvent);
3737 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3738 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3739 QGuiApplication::processEvents();
3741 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3742 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3743 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3746 void tst_qquicktextinput::inputMethodComposing()
3748 QString text = "supercalifragisiticexpialidocious!";
3750 QQuickView view(testFileUrl("inputContext.qml"));
3752 view.requestActivateWindow();
3753 QTest::qWaitForWindowActive(&view);
3754 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3756 QVERIFY(input->hasActiveFocus());
3757 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3759 QCOMPARE(input->isInputMethodComposing(), false);
3761 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3762 QGuiApplication::sendEvent(input, &event);
3764 QCOMPARE(input->isInputMethodComposing(), true);
3765 QCOMPARE(spy.count(), 1);
3768 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3769 QGuiApplication::sendEvent(input, &event);
3771 QCOMPARE(spy.count(), 1);
3774 QInputMethodEvent event;
3775 QGuiApplication::sendEvent(input, &event);
3777 QCOMPARE(input->isInputMethodComposing(), false);
3778 QCOMPARE(spy.count(), 2);
3780 // Changing the text while not composing doesn't alter the composing state.
3781 input->setText(text.mid(0, 16));
3782 QCOMPARE(input->isInputMethodComposing(), false);
3783 QCOMPARE(spy.count(), 2);
3786 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3787 QGuiApplication::sendEvent(input, &event);
3789 QCOMPARE(input->isInputMethodComposing(), true);
3790 QCOMPARE(spy.count(), 3);
3792 // Changing the text while composing cancels composition.
3793 input->setText(text.mid(0, 12));
3794 QCOMPARE(input->isInputMethodComposing(), false);
3795 QCOMPARE(spy.count(), 4);
3797 { // Preedit cursor positioned outside (empty) preedit; composing.
3798 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3799 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3800 QGuiApplication::sendEvent(input, &event);
3802 QCOMPARE(input->isInputMethodComposing(), true);
3803 QCOMPARE(spy.count(), 5);
3806 { // Cursor hidden; composing
3807 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3808 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3809 QGuiApplication::sendEvent(input, &event);
3811 QCOMPARE(input->isInputMethodComposing(), true);
3812 QCOMPARE(spy.count(), 5);
3814 { // Default cursor attributes; composing.
3815 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3816 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3817 QGuiApplication::sendEvent(input, &event);
3819 QCOMPARE(input->isInputMethodComposing(), true);
3820 QCOMPARE(spy.count(), 5);
3822 { // Selections are persisted: not composing
3823 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3824 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3825 QGuiApplication::sendEvent(input, &event);
3827 QCOMPARE(input->isInputMethodComposing(), false);
3828 QCOMPARE(spy.count(), 6);
3830 input->setCursorPosition(12);
3832 { // Formatting applied; composing.
3833 QTextCharFormat format;
3834 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3835 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3836 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3837 QGuiApplication::sendEvent(input, &event);
3839 QCOMPARE(input->isInputMethodComposing(), true);
3840 QCOMPARE(spy.count(), 7);
3843 QInputMethodEvent event;
3844 QGuiApplication::sendEvent(input, &event);
3846 QCOMPARE(input->isInputMethodComposing(), false);
3847 QCOMPARE(spy.count(), 8);
3850 void tst_qquicktextinput::inputMethodUpdate()
3852 PlatformInputContext platformInputContext;
3853 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3854 inputMethodPrivate->testContext = &platformInputContext;
3856 QQuickView view(testFileUrl("inputContext.qml"));
3858 view.requestActivateWindow();
3859 QTest::qWaitForWindowActive(&view);
3860 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3862 QVERIFY(input->hasActiveFocus());
3864 // text change even without cursor position change needs to trigger update
3865 input->setText("test");
3866 platformInputContext.clear();
3867 input->setText("xxxx");
3868 QVERIFY(platformInputContext.m_updateCallCount > 0);
3870 // input method event replacing text
3871 platformInputContext.clear();
3873 QInputMethodEvent inputMethodEvent;
3874 inputMethodEvent.setCommitString("y", -1, 1);
3875 QGuiApplication::sendEvent(input, &inputMethodEvent);
3877 QVERIFY(platformInputContext.m_updateCallCount > 0);
3879 // input method changing selection
3880 platformInputContext.clear();
3882 QList<QInputMethodEvent::Attribute> attributes;
3883 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3884 QInputMethodEvent inputMethodEvent("", attributes);
3885 QGuiApplication::sendEvent(input, &inputMethodEvent);
3887 QVERIFY(input->selectionStart() != input->selectionEnd());
3888 QVERIFY(platformInputContext.m_updateCallCount > 0);
3890 // programmatical selections trigger update
3891 platformInputContext.clear();
3893 QVERIFY(platformInputContext.m_updateCallCount > 0);
3896 platformInputContext.clear();
3897 QFont font = input->font();
3898 font.setBold(!font.bold());
3899 input->setFont(font);
3900 QVERIFY(platformInputContext.m_updateCallCount > 0);
3903 platformInputContext.clear();
3905 QInputMethodEvent inputMethodEvent;
3906 inputMethodEvent.setCommitString("y");
3907 QGuiApplication::sendEvent(input, &inputMethodEvent);
3909 QVERIFY(platformInputContext.m_updateCallCount > 0);
3911 // changing cursor position
3912 platformInputContext.clear();
3913 input->setCursorPosition(0);
3914 QVERIFY(platformInputContext.m_updateCallCount > 0);
3916 // read only disabled input method
3917 platformInputContext.clear();
3918 input->setReadOnly(true);
3919 QVERIFY(platformInputContext.m_updateCallCount > 0);
3920 input->setReadOnly(false);
3922 // no updates while no focus
3923 input->setFocus(false);
3924 platformInputContext.clear();
3925 input->setText("Foo");
3926 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3927 input->setCursorPosition(1);
3928 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3930 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3931 input->setReadOnly(true);
3932 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3935 void tst_qquicktextinput::cursorRectangleSize()
3937 QQuickView *window = new QQuickView(testFileUrl("positionAt.qml"));
3938 QVERIFY(window->rootObject() != 0);
3939 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(window->rootObject());
3941 // make sure cursor rectangle is not at (0,0)
3942 textInput->setX(10);
3943 textInput->setY(10);
3944 textInput->setCursorPosition(3);
3945 QVERIFY(textInput != 0);
3946 textInput->setFocus(true);
3948 window->requestActivateWindow();
3949 QTest::qWaitForWindowActive(window);
3950 QVERIFY(textInput->hasActiveFocus());
3952 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3953 qApp->sendEvent(textInput, &event);
3954 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3956 QRectF cursorRectFromItem = textInput->cursorRectangle();
3957 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3959 // item and input query cursor rectangles match
3960 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3962 // item cursor rectangle and positionToRectangle calculations match
3963 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3965 // item-window transform and input item transform match
3966 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToWindowTransform(), qApp->inputMethod()->inputItemTransform());
3968 // input panel cursorRectangle property and tranformed item cursor rectangle match
3969 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToWindowTransform().mapRect(cursorRectFromItem);
3970 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3975 void tst_qquicktextinput::tripleClickSelectsAll()
3977 QString qmlfile = testFile("positionAt.qml");
3978 QQuickView view(QUrl::fromLocalFile(qmlfile));
3980 view.requestActivateWindow();
3981 QTest::qWaitForWindowActive(&view);
3983 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3986 QLatin1String hello("Hello world!");
3987 input->setSelectByMouse(true);
3988 input->setText(hello);
3990 // Clicking on the same point inside TextInput three times in a row
3991 // should trigger a triple click, thus selecting all the text.
3992 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3993 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3994 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3995 QGuiApplication::processEvents();
3996 QCOMPARE(input->selectedText(), hello);
3998 // Now it simulates user moving the mouse between the second and the third click.
3999 // In this situation, we don't expect a triple click.
4000 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
4001 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4002 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
4003 QGuiApplication::processEvents();
4004 QVERIFY(input->selectedText().isEmpty());
4006 // And now we press the third click too late, so no triple click event is triggered.
4007 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4008 QGuiApplication::processEvents();
4009 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
4010 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
4011 QGuiApplication::processEvents();
4012 QVERIFY(input->selectedText().isEmpty());
4015 void tst_qquicktextinput::QTBUG_19956_data()
4017 QTest::addColumn<QString>("url");
4018 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
4019 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
4023 void tst_qquicktextinput::getText_data()
4025 QTest::addColumn<QString>("text");
4026 QTest::addColumn<QString>("inputMask");
4027 QTest::addColumn<int>("start");
4028 QTest::addColumn<int>("end");
4029 QTest::addColumn<QString>("expectedText");
4031 QTest::newRow("all plain text")
4034 << 0 << standard.at(0).length()
4037 QTest::newRow("plain text sub string")
4041 << standard.at(0).mid(0, 12);
4043 QTest::newRow("plain text sub string reversed")
4047 << standard.at(0).mid(0, 12);
4049 QTest::newRow("plain text cropped beginning")
4053 << standard.at(0).mid(0, 4);
4055 QTest::newRow("plain text cropped end")
4058 << 23 << standard.at(0).length() + 8
4059 << standard.at(0).mid(23);
4061 QTest::newRow("plain text cropped beginning and end")
4064 << -9 << standard.at(0).length() + 4
4068 void tst_qquicktextinput::getText()
4070 QFETCH(QString, text);
4071 QFETCH(QString, inputMask);
4074 QFETCH(QString, expectedText);
4076 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4077 QQmlComponent textInputComponent(&engine);
4078 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4079 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4080 QVERIFY(textInput != 0);
4082 QCOMPARE(textInput->getText(start, end), expectedText);
4085 void tst_qquicktextinput::insert_data()
4087 QTest::addColumn<QString>("text");
4088 QTest::addColumn<QString>("inputMask");
4089 QTest::addColumn<int>("selectionStart");
4090 QTest::addColumn<int>("selectionEnd");
4091 QTest::addColumn<int>("insertPosition");
4092 QTest::addColumn<QString>("insertText");
4093 QTest::addColumn<QString>("expectedText");
4094 QTest::addColumn<int>("expectedSelectionStart");
4095 QTest::addColumn<int>("expectedSelectionEnd");
4096 QTest::addColumn<int>("expectedCursorPosition");
4097 QTest::addColumn<bool>("selectionChanged");
4098 QTest::addColumn<bool>("cursorPositionChanged");
4100 QTest::newRow("at cursor position (beginning)")
4105 << QString("Hello") + standard.at(0)
4109 QTest::newRow("at cursor position (end)")
4112 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
4114 << standard.at(0) + QString("Hello")
4115 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4118 QTest::newRow("at cursor position (middle)")
4123 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4127 QTest::newRow("after cursor position (beginning)")
4132 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4136 QTest::newRow("before cursor position (end)")
4139 << standard.at(0).length() << standard.at(0).length() << 18
4141 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4142 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4145 QTest::newRow("before cursor position (middle)")
4150 << QString("Hello") + standard.at(0)
4154 QTest::newRow("after cursor position (middle)")
4157 << 18 << 18 << standard.at(0).length()
4159 << standard.at(0) + QString("Hello")
4163 QTest::newRow("before selection")
4168 << QString("Hello") + standard.at(0)
4172 QTest::newRow("before reversed selection")
4177 << QString("Hello") + standard.at(0)
4181 QTest::newRow("after selection")
4184 << 14 << 19 << standard.at(0).length()
4186 << standard.at(0) + QString("Hello")
4190 QTest::newRow("after reversed selection")
4193 << 19 << 14 << standard.at(0).length()
4195 << standard.at(0) + QString("Hello")
4199 QTest::newRow("into selection")
4204 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4208 QTest::newRow("into reversed selection")
4213 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4217 QTest::newRow("rich text into plain text")
4221 << QString("<b>Hello</b>")
4222 << QString("<b>Hello</b>") + standard.at(0)
4226 QTest::newRow("before start")
4235 QTest::newRow("past end")
4238 << 0 << 0 << standard.at(0).length() + 3
4244 const QString inputMask = "009.009.009.009";
4245 const QString ip = "192.168.2.14";
4247 QTest::newRow("mask: at cursor position (beginning)")
4252 << QString("125.168.2.14")
4256 QTest::newRow("mask: at cursor position (end)")
4259 << inputMask.length() << inputMask.length() << inputMask.length()
4262 << inputMask.length() << inputMask.length() << inputMask.length()
4265 QTest::newRow("mask: at cursor position (middle)")
4270 << QString("192.167.5.24")
4274 QTest::newRow("mask: after cursor position (beginning)")
4279 << QString("192.167.5.24")
4283 QTest::newRow("mask: before cursor position (end)")
4286 << inputMask.length() << inputMask.length() << 6
4288 << QString("192.167.5.24")
4289 << inputMask.length() << inputMask.length() << inputMask.length()
4292 QTest::newRow("mask: before cursor position (middle)")
4297 << QString("125.168.2.14")
4301 QTest::newRow("mask: after cursor position (middle)")
4310 QTest::newRow("mask: before selection")
4315 << QString("125.168.2.14")
4319 QTest::newRow("mask: before reversed selection")
4324 << QString("125.168.2.14")
4328 QTest::newRow("mask: after selection")
4337 QTest::newRow("mask: after reversed selection")
4346 QTest::newRow("mask: into selection")
4351 << QString("192.167.5.24")
4355 QTest::newRow("mask: into reversed selection")
4360 << QString("192.167.5.24")
4364 QTest::newRow("mask: before start")
4373 QTest::newRow("mask: past end")
4376 << 0 << 0 << ip.length() + 3
4382 QTest::newRow("mask: invalid characters")
4387 << QString("192.168.2.14")
4391 QTest::newRow("mask: mixed validity")
4395 << QString("a1b2c5")
4396 << QString("125.168.2.14")
4401 void tst_qquicktextinput::insert()
4403 QFETCH(QString, text);
4404 QFETCH(QString, inputMask);
4405 QFETCH(int, selectionStart);
4406 QFETCH(int, selectionEnd);
4407 QFETCH(int, insertPosition);
4408 QFETCH(QString, insertText);
4409 QFETCH(QString, expectedText);
4410 QFETCH(int, expectedSelectionStart);
4411 QFETCH(int, expectedSelectionEnd);
4412 QFETCH(int, expectedCursorPosition);
4413 QFETCH(bool, selectionChanged);
4414 QFETCH(bool, cursorPositionChanged);
4416 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4417 QQmlComponent textInputComponent(&engine);
4418 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4419 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4420 QVERIFY(textInput != 0);
4422 textInput->select(selectionStart, selectionEnd);
4424 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4425 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4426 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4427 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4428 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4430 textInput->insert(insertPosition, insertText);
4432 QCOMPARE(textInput->text(), expectedText);
4433 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4435 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4436 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4437 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4439 if (selectionStart > selectionEnd)
4440 qSwap(selectionStart, selectionEnd);
4442 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4443 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4444 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4445 QCOMPARE(textSpy.count() > 0, text != expectedText);
4446 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4449 void tst_qquicktextinput::remove_data()
4451 QTest::addColumn<QString>("text");
4452 QTest::addColumn<QString>("inputMask");
4453 QTest::addColumn<int>("selectionStart");
4454 QTest::addColumn<int>("selectionEnd");
4455 QTest::addColumn<int>("removeStart");
4456 QTest::addColumn<int>("removeEnd");
4457 QTest::addColumn<QString>("expectedText");
4458 QTest::addColumn<int>("expectedSelectionStart");
4459 QTest::addColumn<int>("expectedSelectionEnd");
4460 QTest::addColumn<int>("expectedCursorPosition");
4461 QTest::addColumn<bool>("selectionChanged");
4462 QTest::addColumn<bool>("cursorPositionChanged");
4464 QTest::newRow("from cursor position (beginning)")
4469 << standard.at(0).mid(5)
4473 QTest::newRow("to cursor position (beginning)")
4478 << standard.at(0).mid(5)
4482 QTest::newRow("to cursor position (end)")
4485 << standard.at(0).length() << standard.at(0).length()
4486 << standard.at(0).length() << standard.at(0).length() - 5
4487 << standard.at(0).mid(0, standard.at(0).length() - 5)
4488 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4491 QTest::newRow("to cursor position (end)")
4494 << standard.at(0).length() << standard.at(0).length()
4495 << standard.at(0).length() - 5 << standard.at(0).length()
4496 << standard.at(0).mid(0, standard.at(0).length() - 5)
4497 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4500 QTest::newRow("from cursor position (middle)")
4505 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4509 QTest::newRow("to cursor position (middle)")
4514 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4518 QTest::newRow("after cursor position (beginning)")
4523 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4527 QTest::newRow("before cursor position (end)")
4530 << standard.at(0).length() << standard.at(0).length()
4532 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4533 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4536 QTest::newRow("before cursor position (middle)")
4541 << standard.at(0).mid(5)
4545 QTest::newRow("after cursor position (middle)")
4550 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4554 QTest::newRow("before selection")
4559 << standard.at(0).mid(5)
4563 QTest::newRow("before reversed selection")
4568 << standard.at(0).mid(5)
4572 QTest::newRow("after selection")
4576 << standard.at(0).length() - 5 << standard.at(0).length()
4577 << standard.at(0).mid(0, standard.at(0).length() - 5)
4581 QTest::newRow("after reversed selection")
4585 << standard.at(0).length() - 5 << standard.at(0).length()
4586 << standard.at(0).mid(0, standard.at(0).length() - 5)
4590 QTest::newRow("from selection")
4595 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4599 QTest::newRow("from reversed selection")
4604 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4608 QTest::newRow("cropped beginning")
4613 << standard.at(0).mid(4)
4617 QTest::newRow("cropped end")
4621 << 23 << standard.at(0).length() + 8
4622 << standard.at(0).mid(0, 23)
4626 QTest::newRow("cropped beginning and end")
4630 << -9 << standard.at(0).length() + 4
4635 const QString inputMask = "009.009.009.009";
4636 const QString ip = "192.168.2.14";
4638 QTest::newRow("mask: from cursor position")
4643 << QString("192.16..14")
4647 QTest::newRow("mask: to cursor position")
4652 << QString("19.8.2.14")
4656 QTest::newRow("mask: before cursor position")
4661 << QString("2.168.2.14")
4665 QTest::newRow("mask: after cursor position")
4670 << QString("192.168.2.")
4674 QTest::newRow("mask: before selection")
4679 << QString("2.168.2.14")
4683 QTest::newRow("mask: before reversed selection")
4688 << QString("2.168.2.14")
4692 QTest::newRow("mask: after selection")
4697 << QString("192.168.2.")
4701 QTest::newRow("mask: after reversed selection")
4706 << QString("192.168.2.")
4710 QTest::newRow("mask: from selection")
4715 << QString("192.168..14")
4719 QTest::newRow("mask: from reversed selection")
4724 << QString("192.168..14")
4728 QTest::newRow("mask: cropped beginning")
4733 << QString(".168.2.14")
4737 QTest::newRow("mask: cropped end")
4742 << QString("192.168.2.1")
4746 QTest::newRow("mask: cropped beginning and end")
4756 void tst_qquicktextinput::remove()
4758 QFETCH(QString, text);
4759 QFETCH(QString, inputMask);
4760 QFETCH(int, selectionStart);
4761 QFETCH(int, selectionEnd);
4762 QFETCH(int, removeStart);
4763 QFETCH(int, removeEnd);
4764 QFETCH(QString, expectedText);
4765 QFETCH(int, expectedSelectionStart);
4766 QFETCH(int, expectedSelectionEnd);
4767 QFETCH(int, expectedCursorPosition);
4768 QFETCH(bool, selectionChanged);
4769 QFETCH(bool, cursorPositionChanged);
4771 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4772 QQmlComponent textInputComponent(&engine);
4773 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4774 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4775 QVERIFY(textInput != 0);
4777 textInput->select(selectionStart, selectionEnd);
4779 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4780 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4781 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4782 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4783 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4785 textInput->remove(removeStart, removeEnd);
4787 QCOMPARE(textInput->text(), expectedText);
4788 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4790 if (selectionStart > selectionEnd) //
4791 qSwap(selectionStart, selectionEnd);
4793 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4794 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4795 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4797 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4798 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4799 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4800 QCOMPARE(textSpy.count() > 0, text != expectedText);
4802 if (cursorPositionChanged) //
4803 QVERIFY(cursorPositionSpy.count() > 0);
4806 void tst_qquicktextinput::keySequence_data()
4808 QTest::addColumn<QString>("text");
4809 QTest::addColumn<QKeySequence>("sequence");
4810 QTest::addColumn<int>("selectionStart");
4811 QTest::addColumn<int>("selectionEnd");
4812 QTest::addColumn<int>("cursorPosition");
4813 QTest::addColumn<QString>("expectedText");
4814 QTest::addColumn<QString>("selectedText");
4815 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4816 QTest::addColumn<Qt::Key>("layoutDirection");
4818 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4820 QTest::newRow("select all")
4821 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4822 << 44 << standard.at(0) << standard.at(0)
4823 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4824 QTest::newRow("select start of line")
4825 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4826 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4827 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4828 QTest::newRow("select start of block")
4829 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4830 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4831 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4832 QTest::newRow("select end of line")
4833 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4834 << 44 << standard.at(0) << standard.at(0).mid(5)
4835 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4836 QTest::newRow("select end of document") // ### Not handled.
4837 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4838 << 3 << standard.at(0) << QString()
4839 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4840 QTest::newRow("select end of block")
4841 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4842 << 44 << standard.at(0) << standard.at(0).mid(18)
4843 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4844 QTest::newRow("delete end of line")
4845 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4846 << 24 << standard.at(0).mid(0, 24) << QString()
4847 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4848 QTest::newRow("move to start of line")
4849 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4850 << 0 << standard.at(0) << QString()
4851 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4852 QTest::newRow("move to start of block")
4853 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4854 << 0 << standard.at(0) << QString()
4855 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4856 QTest::newRow("move to next char")
4857 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4858 << 13 << standard.at(0) << QString()
4859 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4860 QTest::newRow("move to previous char (ltr)")
4861 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4862 << 2 << standard.at(0) << QString()
4863 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4864 QTest::newRow("move to previous char (rtl)")
4865 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4866 << 4 << standard.at(0) << QString()
4867 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4868 QTest::newRow("move to previous char with selection")
4869 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4870 << 3 << standard.at(0) << QString()
4871 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4872 QTest::newRow("select next char (ltr)")
4873 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4874 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4875 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4876 QTest::newRow("select next char (rtl)")
4877 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4878 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4879 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4880 QTest::newRow("select previous char (ltr)")
4881 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4882 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4883 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4884 QTest::newRow("select previous char (rtl)")
4885 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4886 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4887 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4888 QTest::newRow("move to next word (ltr)")
4889 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4890 << 10 << standard.at(0) << QString()
4891 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4892 QTest::newRow("move to next word (rtl)")
4893 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4894 << 4 << standard.at(0) << QString()
4895 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4896 QTest::newRow("move to next word (password,ltr)")
4897 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4898 << 44 << standard.at(0) << QString()
4899 << QQuickTextInput::Password << Qt::Key_Direction_L;
4900 QTest::newRow("move to next word (password,rtl)")
4901 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4902 << 0 << standard.at(0) << QString()
4903 << QQuickTextInput::Password << Qt::Key_Direction_R;
4904 QTest::newRow("move to previous word (ltr)")
4905 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4906 << 4 << standard.at(0) << QString()
4907 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4908 QTest::newRow("move to previous word (rlt)")
4909 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4910 << 10 << standard.at(0) << QString()
4911 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4912 QTest::newRow("move to previous word (password,ltr)")
4913 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4914 << 0 << standard.at(0) << QString()
4915 << QQuickTextInput::Password << Qt::Key_Direction_L;
4916 QTest::newRow("move to previous word (password,rtl)")
4917 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4918 << 44 << standard.at(0) << QString()
4919 << QQuickTextInput::Password << Qt::Key_Direction_R;
4920 QTest::newRow("select next word")
4921 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4922 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4923 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4924 QTest::newRow("select previous word")
4925 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4926 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4927 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4928 QTest::newRow("delete (selection)")
4929 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4930 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4931 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4932 QTest::newRow("delete (no selection)")
4933 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4934 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4935 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4936 QTest::newRow("delete end of word")
4937 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4938 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4939 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4940 QTest::newRow("delete start of word")
4941 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4942 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4943 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4946 void tst_qquicktextinput::keySequence()
4948 QFETCH(QString, text);
4949 QFETCH(QKeySequence, sequence);
4950 QFETCH(int, selectionStart);
4951 QFETCH(int, selectionEnd);
4952 QFETCH(int, cursorPosition);
4953 QFETCH(QString, expectedText);
4954 QFETCH(QString, selectedText);
4955 QFETCH(QQuickTextInput::EchoMode, echoMode);
4956 QFETCH(Qt::Key, layoutDirection);
4958 if (sequence.isEmpty()) {
4959 QSKIP("Key sequence is undefined");
4962 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4963 QQmlComponent textInputComponent(&engine);
4964 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4965 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4966 QVERIFY(textInput != 0);
4967 textInput->setEchoMode(echoMode);
4969 QQuickWindow window;
4970 textInput->setParentItem(window.rootItem());
4972 window.requestActivateWindow();
4973 QTest::qWaitForWindowActive(&window);
4974 QVERIFY(textInput->hasActiveFocus());
4976 simulateKey(&window, layoutDirection);
4978 textInput->select(selectionStart, selectionEnd);
4980 simulateKeys(&window, sequence);
4982 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4983 QCOMPARE(textInput->text(), expectedText);
4984 QCOMPARE(textInput->selectedText(), selectedText);
4988 #define REPLACE_UNTIL_END 1
4990 void tst_qquicktextinput::undo_data()
4992 QTest::addColumn<QStringList>("insertString");
4993 QTest::addColumn<IntList>("insertIndex");
4994 QTest::addColumn<IntList>("insertMode");
4995 QTest::addColumn<QStringList>("expectedString");
4996 QTest::addColumn<bool>("use_keys");
4998 for (int i=0; i<2; i++) {
4999 QString keys_str = "keyboard";
5000 bool use_keys = true;
5002 keys_str = "insert";
5007 IntList insertIndex;
5009 QStringList insertString;
5010 QStringList expectedString;
5013 insertMode << NORMAL;
5014 insertString << "1";
5017 insertMode << NORMAL;
5018 insertString << "5";
5021 insertMode << NORMAL;
5022 insertString << "3";
5025 insertMode << NORMAL;
5026 insertString << "2";
5029 insertMode << NORMAL;
5030 insertString << "4";
5032 expectedString << "12345";
5033 expectedString << "1235";
5034 expectedString << "135";
5035 expectedString << "15";
5036 expectedString << "";
5038 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
5046 IntList insertIndex;
5048 QStringList insertString;
5049 QStringList expectedString;
5052 insertMode << NORMAL;
5053 insertString << "World"; // World
5056 insertMode << NORMAL;
5057 insertString << "Hello"; // HelloWorld
5060 insertMode << NORMAL;
5061 insertString << "Well"; // WellHelloWorld
5064 insertMode << NORMAL;
5065 insertString << "There"; // WellHelloThereWorld;
5067 expectedString << "WellHelloThereWorld";
5068 expectedString << "WellHelloWorld";
5069 expectedString << "HelloWorld";
5070 expectedString << "World";
5071 expectedString << "";
5073 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
5081 IntList insertIndex;
5083 QStringList insertString;
5084 QStringList expectedString;
5087 insertMode << NORMAL;
5088 insertString << "Ensuring";
5091 insertMode << NORMAL;
5092 insertString << " instan";
5095 insertMode << NORMAL;
5096 insertString << "an ";
5099 insertMode << REPLACE_UNTIL_END;
5100 insertString << " unique instance.";
5102 expectedString << "Ensuring a unique instance.";
5103 expectedString << "Ensuring an instan";
5104 expectedString << "Ensuring instan";
5105 expectedString << "";
5107 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
5117 void tst_qquicktextinput::undo()
5119 QFETCH(QStringList, insertString);
5120 QFETCH(IntList, insertIndex);
5121 QFETCH(IntList, insertMode);
5122 QFETCH(QStringList, expectedString);
5124 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5125 QQmlComponent textInputComponent(&engine);
5126 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5127 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5128 QVERIFY(textInput != 0);
5130 QQuickWindow window;
5131 textInput->setParentItem(window.rootItem());
5133 window.requestActivateWindow();
5134 QTest::qWaitForWindowActive(&window);
5135 QVERIFY(textInput->hasActiveFocus());
5137 QVERIFY(!textInput->canUndo());
5139 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
5143 // STEP 1: First build up an undo history by inserting or typing some strings...
5144 for (i = 0; i < insertString.size(); ++i) {
5145 if (insertIndex[i] > -1)
5146 textInput->setCursorPosition(insertIndex[i]);
5148 // experimental stuff
5149 if (insertMode[i] == REPLACE_UNTIL_END) {
5150 textInput->select(insertIndex[i], insertIndex[i] + 8);
5152 // This is what I actually want...
5153 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
5156 for (int j = 0; j < insertString.at(i).length(); j++)
5157 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5160 QCOMPARE(spy.count(), 1);
5162 // STEP 2: Next call undo several times and see if we can restore to the previous state
5163 for (i = 0; i < expectedString.size() - 1; ++i) {
5164 QCOMPARE(textInput->text(), expectedString[i]);
5165 QVERIFY(textInput->canUndo());
5169 // STEP 3: Verify that we have undone everything
5170 QVERIFY(textInput->text().isEmpty());
5171 QVERIFY(!textInput->canUndo());
5172 QCOMPARE(spy.count(), 2);
5175 void tst_qquicktextinput::redo_data()
5177 QTest::addColumn<QStringList>("insertString");
5178 QTest::addColumn<IntList>("insertIndex");
5179 QTest::addColumn<QStringList>("expectedString");
5182 IntList insertIndex;
5183 QStringList insertString;
5184 QStringList expectedString;
5187 insertString << "World"; // World
5189 insertString << "Hello"; // HelloWorld
5191 insertString << "Well"; // WellHelloWorld
5193 insertString << "There"; // WellHelloThereWorld;
5195 expectedString << "World";
5196 expectedString << "HelloWorld";
5197 expectedString << "WellHelloWorld";
5198 expectedString << "WellHelloThereWorld";
5200 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
5204 void tst_qquicktextinput::redo()
5206 QFETCH(QStringList, insertString);
5207 QFETCH(IntList, insertIndex);
5208 QFETCH(QStringList, expectedString);
5210 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5211 QQmlComponent textInputComponent(&engine);
5212 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5213 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5214 QVERIFY(textInput != 0);
5216 QQuickWindow window;
5217 textInput->setParentItem(window.rootItem());
5219 window.requestActivateWindow();
5220 QTest::qWaitForWindowActive(&window);
5222 QVERIFY(textInput->hasActiveFocus());
5223 QVERIFY(!textInput->canUndo());
5224 QVERIFY(!textInput->canRedo());
5226 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5229 // inserts the diff strings at diff positions
5230 for (i = 0; i < insertString.size(); ++i) {
5231 if (insertIndex[i] > -1)
5232 textInput->setCursorPosition(insertIndex[i]);
5233 for (int j = 0; j < insertString.at(i).length(); j++)
5234 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5235 QVERIFY(textInput->canUndo());
5236 QVERIFY(!textInput->canRedo());
5239 QCOMPARE(spy.count(), 0);
5242 while (!textInput->text().isEmpty()) {
5243 QVERIFY(textInput->canUndo());
5245 QVERIFY(textInput->canRedo());
5248 QCOMPARE(spy.count(), 1);
5250 for (i = 0; i < expectedString.size(); ++i) {
5251 QVERIFY(textInput->canRedo());
5253 QCOMPARE(textInput->text() , expectedString[i]);
5254 QVERIFY(textInput->canUndo());
5256 QVERIFY(!textInput->canRedo());
5257 QCOMPARE(spy.count(), 2);
5260 void tst_qquicktextinput::undo_keypressevents_data()
5262 QTest::addColumn<KeyList>("keys");
5263 QTest::addColumn<QStringList>("expectedString");
5267 QStringList expectedString;
5270 << QKeySequence::MoveToStartOfLine
5277 << QKeySequence::MoveToEndOfLine
5280 expectedString << "BEVERYAFRAID!";
5281 expectedString << "BEVERYAFRAID";
5282 expectedString << "VERYAFRAID";
5283 expectedString << "AFRAID";
5285 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5288 QStringList expectedString;
5291 keys << "1234" << QKeySequence::MoveToStartOfLine
5293 << Qt::Key_Right << Qt::Key_Right
5295 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5299 expectedString << "12";
5300 expectedString << "1234";
5302 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5305 QStringList expectedString;
5309 << QKeySequence::MoveToStartOfLine
5311 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5312 << Qt::Key_Backspace
5313 << QKeySequence::Undo
5315 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5318 expectedString << "AB";
5319 expectedString << "AB12";
5321 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5324 QStringList expectedString;
5329 << Qt::Key_Left << Qt::Key_Left
5333 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5334 // overwriting '1234' with '5'
5336 // undoing deletion of 'AB'
5337 << QKeySequence::Undo
5338 // overwriting '1234' with '6'
5341 expectedString << "ab6cd";
5342 // for versions previous to 3.2 we overwrite needed two undo operations
5343 expectedString << "ab1234cd";
5344 expectedString << "abcd";
5346 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5349 QStringList expectedString;
5354 << Qt::Key_Backspace;
5356 expectedString << "AB";
5357 expectedString << "ABC";
5359 QTest::newRow("Inserts,backspace") << keys << expectedString;
5362 QStringList expectedString;
5366 << Qt::Key_Backspace
5370 expectedString << "ABZ";
5371 expectedString << "AB";
5372 expectedString << "ABC";
5374 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5377 QStringList expectedString;
5380 keys << "123" << QKeySequence::MoveToStartOfLine
5382 << QKeySequence::SelectEndOfLine
5383 // overwriting '123' with 'ABC'
5386 expectedString << "ABC";
5387 expectedString << "123";
5389 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5392 QStringList expectedString;
5396 << QKeySequence::Undo
5397 << QKeySequence::Redo;
5399 expectedString << "123";
5400 expectedString << QString();
5402 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5405 QStringList expectedString;
5407 keys << "hello world"
5408 << (Qt::Key_Backspace | Qt::ControlModifier)
5409 << QKeySequence::Undo
5410 << QKeySequence::Redo
5419 QTest::newRow("Insert,delete previous word,undo,redo,insert") << keys << expectedString;
5422 QStringList expectedString;
5424 keys << "hello world"
5425 << QKeySequence::SelectPreviousWord
5426 << (Qt::Key_Backspace)
5427 << QKeySequence::Undo
5435 QTest::newRow("Insert,select previous word,remove,undo,insert") << keys << expectedString;
5438 QStringList expectedString;
5440 keys << "hello world"
5441 << QKeySequence::DeleteStartOfWord
5442 << QKeySequence::Undo
5446 << "hello worldhello"
5450 QTest::newRow("Insert,delete previous word,undo,insert") << keys << expectedString;
5453 QStringList expectedString;
5455 keys << "hello world"
5456 << QKeySequence::MoveToPreviousWord
5457 << QKeySequence::DeleteEndOfWord
5458 << QKeySequence::Undo
5462 << "hello helloworld"
5466 QTest::newRow("Insert,move,delete next word,undo,insert") << keys << expectedString;
5468 if (!QKeySequence(QKeySequence::DeleteEndOfLine).isEmpty()) { // X11 only.
5470 QStringList expectedString;
5472 keys << "hello world"
5473 << QKeySequence::MoveToStartOfLine
5475 << QKeySequence::DeleteEndOfLine
5476 << QKeySequence::Undo
5480 << "hhelloello world"
5484 QTest::newRow("Insert,move,delete end of line,undo,insert") << keys << expectedString;
5487 QStringList expectedString;
5489 keys << "hello world"
5490 << QKeySequence::MoveToPreviousWord
5491 << (Qt::Key_Left | Qt::ShiftModifier)
5492 << (Qt::Key_Left | Qt::ShiftModifier)
5493 << QKeySequence::DeleteEndOfWord
5494 << QKeySequence::Undo
5502 QTest::newRow("Insert,move,select,delete next word,undo,insert") << keys << expectedString;
5505 bool canCopyPaste = PlatformQuirks::isClipboardAvailable();
5510 << QKeySequence(QKeySequence::SelectStartOfLine)
5511 << QKeySequence(QKeySequence::Cut)
5513 << QKeySequence(QKeySequence::Paste);
5514 QStringList expectedString = QStringList()
5519 QTest::newRow("Cut,paste") << keys << expectedString;
5524 << QKeySequence(QKeySequence::SelectStartOfLine)
5525 << QKeySequence(QKeySequence::Copy)
5527 << QKeySequence(QKeySequence::Paste);
5528 QStringList expectedString = QStringList()
5533 QTest::newRow("Copy,paste") << keys << expectedString;
5537 void tst_qquicktextinput::undo_keypressevents()
5539 QFETCH(KeyList, keys);
5540 QFETCH(QStringList, expectedString);
5542 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5543 QQmlComponent textInputComponent(&engine);
5544 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5545 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5546 QVERIFY(textInput != 0);
5548 QQuickWindow window;
5549 textInput->setParentItem(window.rootItem());
5551 window.requestActivateWindow();
5552 QTest::qWaitForWindowActive(&window);
5553 QVERIFY(textInput->hasActiveFocus());
5555 simulateKeys(&window, keys);
5557 for (int i = 0; i < expectedString.size(); ++i) {
5558 QCOMPARE(textInput->text() , expectedString[i]);
5561 QVERIFY(textInput->text().isEmpty());
5564 void tst_qquicktextinput::backspaceSurrogatePairs()
5566 // Test backspace, and delete remove both characters in a surrogate pair.
5567 static const quint16 textData[] = { 0xd800, 0xdf00, 0xd800, 0xdf01, 0xd800, 0xdf02, 0xd800, 0xdf03, 0xd800, 0xdf04 };
5568 const QString text = QString::fromUtf16(textData, lengthOf(textData));
5570 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5571 QQmlComponent textInputComponent(&engine);
5572 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5573 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5574 QVERIFY(textInput != 0);
5575 textInput->setText(text);
5576 textInput->setCursorPosition(text.length());
5578 QQuickWindow window;
5579 textInput->setParentItem(window.contentItem());
5581 window.requestActivateWindow();
5582 QVERIFY(QTest::qWaitForWindowActive(&window));
5583 QCOMPARE(QGuiApplication::focusWindow(), &window);
5585 for (int i = text.length(); i >= 0; i -= 2) {
5586 QCOMPARE(textInput->text(), text.mid(0, i));
5587 QTest::keyClick(&window, Qt::Key_Backspace, Qt::NoModifier);
5589 QCOMPARE(textInput->text(), QString());
5591 textInput->setText(text);
5592 textInput->setCursorPosition(0);
5594 for (int i = 0; i < text.length(); i += 2) {
5595 QCOMPARE(textInput->text(), text.mid(i));
5596 QTest::keyClick(&window, Qt::Key_Delete, Qt::NoModifier);
5598 QCOMPARE(textInput->text(), QString());
5601 void tst_qquicktextinput::QTBUG_19956()
5603 QFETCH(QString, url);
5605 QQuickView window(testFileUrl(url));
5607 window.requestActivateWindow();
5608 QTest::qWaitForWindowActive(&window);
5609 QVERIFY(window.rootObject() != 0);
5610 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5612 input->setFocus(true);
5613 QVERIFY(input->hasActiveFocus());
5615 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 30);
5616 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5617 QCOMPARE(window.rootObject()->property("text").toString(), QString("20"));
5618 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5620 window.rootObject()->setProperty("topvalue", 15);
5621 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 15);
5622 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5624 window.rootObject()->setProperty("topvalue", 25);
5625 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 25);
5626 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5628 window.rootObject()->setProperty("bottomvalue", 21);
5629 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 21);
5630 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5632 window.rootObject()->setProperty("bottomvalue", 10);
5633 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5634 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5637 void tst_qquicktextinput::QTBUG_19956_regexp()
5639 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5641 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5642 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5644 QQuickView window(url);
5646 window.requestActivateWindow();
5647 QTest::qWaitForWindowActive(&window);
5648 QVERIFY(window.rootObject() != 0);
5649 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5651 input->setFocus(true);
5652 QVERIFY(input->hasActiveFocus());
5654 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5655 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5656 QCOMPARE(window.rootObject()->property("text").toString(), QString("abc"));
5657 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5659 window.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5660 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5661 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5663 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5664 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5665 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5668 void tst_qquicktextinput::implicitSize_data()
5670 QTest::addColumn<QString>("text");
5671 QTest::addColumn<QString>("wrap");
5672 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5673 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5676 void tst_qquicktextinput::implicitSize()
5678 QFETCH(QString, text);
5679 QFETCH(QString, wrap);
5680 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5681 QQmlComponent textComponent(&engine);
5682 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5683 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5685 QVERIFY(textObject->width() < textObject->implicitWidth());
5686 QVERIFY(textObject->height() == textObject->implicitHeight());
5688 textObject->resetWidth();
5689 QVERIFY(textObject->width() == textObject->implicitWidth());
5690 QVERIFY(textObject->height() == textObject->implicitHeight());
5693 void tst_qquicktextinput::implicitSizeBinding_data()
5695 implicitSize_data();
5698 void tst_qquicktextinput::implicitSizeBinding()
5700 QFETCH(QString, text);
5701 QFETCH(QString, wrap);
5702 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5703 QQmlComponent textComponent(&engine);
5704 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5705 QScopedPointer<QObject> object(textComponent.create());
5706 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5708 QCOMPARE(textObject->width(), textObject->implicitWidth());
5709 QCOMPARE(textObject->height(), textObject->implicitHeight());
5711 textObject->resetWidth();
5712 QCOMPARE(textObject->width(), textObject->implicitWidth());
5713 QCOMPARE(textObject->height(), textObject->implicitHeight());
5715 textObject->resetHeight();
5716 QCOMPARE(textObject->width(), textObject->implicitWidth());
5717 QCOMPARE(textObject->height(), textObject->implicitHeight());
5721 void tst_qquicktextinput::negativeDimensions()
5723 // Verify this doesn't assert during initialization.
5724 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5725 QScopedPointer<QObject> o(component.create());
5727 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5729 QCOMPARE(input->width(), qreal(-1));
5730 QCOMPARE(input->height(), qreal(-1));
5734 void tst_qquicktextinput::setInputMask_data()
5736 QTest::addColumn<QString>("mask");
5737 QTest::addColumn<QString>("input");
5738 QTest::addColumn<QString>("expectedText");
5739 QTest::addColumn<QString>("expectedDisplay");
5740 QTest::addColumn<bool>("insert_text");
5742 // both keyboard and insert()
5743 for (int i=0; i<2; i++) {
5744 bool insert_text = i==0 ? false : true;
5745 QString insert_mode = "keys ";
5747 insert_mode = "insert ";
5749 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5750 << QString("000.000.000.000")
5751 << QString("127.0.0.1")
5752 << QString("127.0.0.1")
5753 << QString("127.0 .0 .1 ")
5754 << bool(insert_text);
5755 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5756 << QString("HH:HH:HH:HH:HH:HH;#")
5757 << QString("00:E0:81:21:9E:8E")
5758 << QString("00:E0:81:21:9E:8E")
5759 << QString("00:E0:81:21:9E:8E")
5760 << bool(insert_text);
5761 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5762 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5763 << QString("AAe081219E8E")
5764 << QString("aa:E0:81:21:9E:8E")
5765 << QString("aa:E0:81:21:9E:8E")
5766 << bool(insert_text);
5767 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5768 << QString("BBBBBBBB;0")
5769 << QString("11011001")
5771 << QString("11011001")
5772 << bool(insert_text);
5773 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5774 << QString("bbbb.bbbb;-")
5775 << QString("110. 0001")
5776 << QString("110.0001")
5777 << QString("110-.0001")
5778 << bool(insert_text);
5779 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5780 << QString("000.000.000.000;0")
5781 << QString("127.0.0.1")
5782 << QString("127...1")
5783 << QString("127.000.000.100")
5784 << bool(insert_text);
5785 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5786 << QString("000.000.000.000")
5787 << QString(".0.0.1")
5788 << QString(".0.0.1")
5789 << QString(" .0 .0 .1 ")
5790 << bool(insert_text);
5791 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5792 << QString("000.000.000.000")
5795 << QString(" . . . ")
5796 << bool(insert_text);
5797 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5798 << QString("000.000.000.000;#")
5801 << QString("###.###.###.###")
5802 << bool(insert_text);
5803 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5804 << QString("000.000.000.000")
5805 << QString("1234123412341234")
5806 << QString("123.412.341.234")
5807 << QString("123.412.341.234")
5808 << bool(insert_text);
5809 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5814 << bool(insert_text);
5815 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5820 << bool(insert_text);
5822 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5827 << bool(insert_text);
5828 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5829 << QString("!A!A!A!A")
5833 << bool(insert_text);
5834 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5839 << bool(insert_text);
5841 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5842 << QString("{n}[0]")
5846 << bool(insert_text);
5847 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5848 << QString("\\\\N\\\\n00")
5852 << bool(insert_text);
5853 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5854 << QString("\\\\\\\\00")
5858 << bool(insert_text);
5859 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5860 << QString("\\\\(00\\\\)")
5864 << bool(insert_text);
5866 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5867 << QString(">AAAA<AAAA!AAAA")
5868 << QString("AbCdEfGhIjKl")
5869 << QString("ABCDefghIjKl")
5870 << QString("ABCDefghIjKl")
5871 << bool(insert_text);
5872 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5873 << QString(">aaaa<aaaa!aaaa")
5874 << QString("AbCdEfGhIjKl")
5875 << QString("ABCDefghIjKl")
5876 << QString("ABCDefghIjKl")
5877 << bool(insert_text);
5879 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5880 << QString(">A<A<A>A>A<A!A!A")
5881 << QString("AbCdEFGH")
5882 << QString("AbcDEfGH")
5883 << QString("AbcDEfGH")
5884 << bool(insert_text);
5885 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5886 << QString(">A<A<A>A>A<A!A!A")
5887 << QString("aBcDefgh")
5888 << QString("AbcDEfgh")
5889 << QString("AbcDEfgh")
5890 << bool(insert_text);
5891 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5892 << QString(">a<a<a>a>a<a!a!a")
5893 << QString("AbCdEFGH")
5894 << QString("AbcDEfGH")
5895 << QString("AbcDEfGH")
5896 << bool(insert_text);
5897 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5898 << QString(">a<a<a>a>a<a!a!a")
5899 << QString("aBcDefgh")
5900 << QString("AbcDEfgh")
5901 << QString("AbcDEfgh")
5902 << bool(insert_text);
5903 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5904 << QString(">H<H<H>H>H<H!H!H")
5905 << QString("aBcDef01")
5906 << QString("AbcDEf01")
5907 << QString("AbcDEf01")
5908 << bool(insert_text);
5909 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5910 << QString(">h<h<h>h>h<h!h!h")
5911 << QString("aBcDef92")
5912 << QString("AbcDEf92")
5913 << QString("AbcDEf92")
5914 << bool(insert_text);
5916 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5917 << QString("AAAAAAAA")
5918 << QString("A2#a;.0!")
5921 << bool(insert_text);
5922 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5924 << QString("f4f4f4f4")
5927 << bool(insert_text);
5928 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5929 << QString("9999;0")
5933 << bool(insert_text);
5937 void tst_qquicktextinput::setInputMask()
5939 QFETCH(QString, mask);
5940 QFETCH(QString, input);
5941 QFETCH(QString, expectedText);
5942 QFETCH(QString, expectedDisplay);
5943 QFETCH(bool, insert_text);
5945 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5946 QQmlComponent textInputComponent(&engine);
5947 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5948 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5949 QVERIFY(textInput != 0);
5951 // then either insert using insert() or keyboard
5953 textInput->insert(0, input);
5955 QQuickWindow window;
5956 textInput->setParentItem(window.rootItem());
5958 window.requestActivateWindow();
5959 QTest::qWaitForWindowActive(&window);
5960 QVERIFY(textInput->hasActiveFocus());
5962 simulateKey(&window, Qt::Key_Home);
5963 for (int i = 0; i < input.length(); i++)
5964 QTest::keyClick(&window, input.at(i).toLatin1());
5967 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5968 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5970 QCOMPARE(textInput->text(), expectedText);
5971 QCOMPARE(textInput->displayText(), expectedDisplay);
5974 void tst_qquicktextinput::inputMask_data()
5976 QTest::addColumn<QString>("mask");
5977 QTest::addColumn<QString>("expectedMask");
5979 // if no mask is set a nul string should be returned
5980 QTest::newRow("nul 1") << QString("") << QString();
5981 QTest::newRow("nul 2") << QString() << QString();
5983 // try different masks
5984 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5985 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5986 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5987 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5989 // set an invalid input mask...
5990 // the current behaviour is that this exact (faulty) string is returned.
5991 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5993 // verify that we can unset the mask again
5994 QTest::newRow("unset") << QString("") << QString();
5997 void tst_qquicktextinput::inputMask()
5999 QFETCH(QString, mask);
6000 QFETCH(QString, expectedMask);
6002 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6003 QQmlComponent textInputComponent(&engine);
6004 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6005 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6006 QVERIFY(textInput != 0);
6008 QCOMPARE(textInput->inputMask(), expectedMask);
6011 void tst_qquicktextinput::clearInputMask()
6013 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
6014 QQmlComponent textInputComponent(&engine);
6015 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6016 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6017 QVERIFY(textInput != 0);
6019 QVERIFY(!textInput->inputMask().isEmpty());
6020 textInput->setInputMask(QString());
6021 QCOMPARE(textInput->inputMask(), QString());
6024 void tst_qquicktextinput::keypress_inputMask_data()
6026 QTest::addColumn<QString>("mask");
6027 QTest::addColumn<KeyList>("keys");
6028 QTest::addColumn<QString>("expectedText");
6029 QTest::addColumn<QString>("expectedDisplayText");
6033 // inserting 'A1.2B'
6034 keys << Qt::Key_Home << "A1.2B";
6035 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
6040 keys << Qt::Key_Home << "0!P3";
6041 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
6046 keys << Qt::Key_Home
6048 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6052 // selecting all and delete
6053 keys << Qt::Key_Home
6054 << Key(Qt::ShiftModifier, Qt::Key_End)
6056 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6060 // inserting '12.12' then two backspaces
6061 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
6062 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
6067 keys << Qt::Key_Home << "12ab";
6068 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
6072 void tst_qquicktextinput::keypress_inputMask()
6074 QFETCH(QString, mask);
6075 QFETCH(KeyList, keys);
6076 QFETCH(QString, expectedText);
6077 QFETCH(QString, expectedDisplayText);
6079 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6080 QQmlComponent textInputComponent(&engine);
6081 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6082 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6083 QVERIFY(textInput != 0);
6085 QQuickWindow window;
6086 textInput->setParentItem(window.rootItem());
6088 window.requestActivateWindow();
6089 QTest::qWaitForWindowActive(&window);
6090 QVERIFY(textInput->hasActiveFocus());
6092 simulateKeys(&window, keys);
6094 QCOMPARE(textInput->text(), expectedText);
6095 QCOMPARE(textInput->displayText(), expectedDisplayText);
6099 void tst_qquicktextinput::hasAcceptableInputMask_data()
6101 QTest::addColumn<QString>("optionalMask");
6102 QTest::addColumn<QString>("requiredMask");
6103 QTest::addColumn<QString>("invalid");
6104 QTest::addColumn<QString>("valid");
6106 QTest::newRow("Alphabetic optional and required")
6107 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
6108 QTest::newRow("Alphanumeric optional and require")
6109 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
6110 QTest::newRow("Any optional and required")
6111 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
6112 QTest::newRow("Numeric (0-9) required")
6113 << QString("0000") << QString("9999") << QString("11") << QString("1138");
6114 QTest::newRow("Numeric (1-9) optional and required")
6115 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
6118 void tst_qquicktextinput::hasAcceptableInputMask()
6120 QFETCH(QString, optionalMask);
6121 QFETCH(QString, requiredMask);
6122 QFETCH(QString, invalid);
6123 QFETCH(QString, valid);
6125 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + optionalMask + "\" }";
6126 QQmlComponent textInputComponent(&engine);
6127 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6128 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6129 QVERIFY(textInput != 0);
6131 // test that invalid input (for required) work for optionalMask
6132 textInput->setText(invalid);
6133 QVERIFY(textInput->hasAcceptableInput());
6135 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
6136 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
6138 // test requiredMask
6139 textInput->setInputMask(requiredMask);
6140 textInput->setText(invalid);
6141 QVERIFY(!textInput->hasAcceptableInput());
6143 textInput->setText(valid);
6144 QVERIFY(textInput->hasAcceptableInput());
6147 void tst_qquicktextinput::maskCharacter_data()
6149 QTest::addColumn<QString>("mask");
6150 QTest::addColumn<QString>("input");
6151 QTest::addColumn<bool>("expectedValid");
6153 QTest::newRow("Hex") << QString("H")
6154 << QString("0123456789abcdefABCDEF") << true;
6155 QTest::newRow("hex") << QString("h")
6156 << QString("0123456789abcdefABCDEF") << true;
6157 QTest::newRow("HexInvalid") << QString("H")
6158 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6160 QTest::newRow("hexInvalid") << QString("h")
6161 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6163 QTest::newRow("Bin") << QString("B")
6164 << QString("01") << true;
6165 QTest::newRow("bin") << QString("b")
6166 << QString("01") << true;
6167 QTest::newRow("BinInvalid") << QString("B")
6168 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6170 QTest::newRow("binInvalid") << QString("b")
6171 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6175 void tst_qquicktextinput::maskCharacter()
6177 QFETCH(QString, mask);
6178 QFETCH(QString, input);
6179 QFETCH(bool, expectedValid);
6181 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + mask + "\" }";
6182 QQmlComponent textInputComponent(&engine);
6183 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6184 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6185 QVERIFY(textInput != 0);
6187 for (int i = 0; i < input.size(); ++i) {
6188 QString in = QString(input.at(i));
6189 QString expected = expectedValid ? in : QString();
6190 textInput->setText(QString(input.at(i)));
6191 QCOMPARE(textInput->text(), expected);
6195 QTEST_MAIN(tst_qquicktextinput)
6197 #include "tst_qquicktextinput.moc"