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,ltr")
915 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 0 << 6 << false;
916 QTest::newRow("Hello<(,)> |words,rtl")
917 << standard[2] << 6 << 5 << QQuickTextInput::SelectWords << 5 << 6 << false;
918 QTest::newRow("<Hello(, )>world|words,ltr")
919 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
920 QTest::newRow("Hello<(, )world>|words,rtl")
921 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
922 QTest::newRow("<Hel(lo, )>world|words,ltr")
923 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
924 QTest::newRow("<Hel(lo, )world>|words,rtl")
925 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
926 QTest::newRow("<Hel(lo)>,|words")
927 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
928 QTest::newRow("Hello<()>,|words")
929 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
930 QTest::newRow("Hello,<()>|words")
931 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
932 QTest::newRow("Hello,<( )>world|words,ltr")
933 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 6 << 7 << false;
934 QTest::newRow("Hello,<( )world>|words,rtl")
935 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
936 QTest::newRow("Hello,<( world)>|words")
937 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 6 << 12 << true;
938 QTest::newRow("Hello,<( world!)>|words")
939 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 6 << 13 << true;
940 QTest::newRow("<Hello(, world!)>|words,ltr")
941 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 0 << 13 << false;
942 QTest::newRow("Hello<(, world!)>|words,rtl")
943 << standard[2] << 13 << 5 << QQuickTextInput::SelectWords << 5 << 13 << false;
944 QTest::newRow("<world(!)>|words,ltr")
945 << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 7 << 13 << false;
946 QTest::newRow("world<(!)>|words,rtl")
947 << standard[2] << 13 << 12 << QQuickTextInput::SelectWords << 12 << 13 << false;
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,ltr")
954 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << false;
955 QTest::newRow("<(,)olleH> |words,rtl")
956 << standard[3] << 8 << 7 << QQuickTextInput::SelectWords << 7 << 13 << false;
957 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
958 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
959 QTest::newRow("dlrow<( ,)olleH>|words,rtl")
960 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
961 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
962 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
963 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
964 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
965 QTest::newRow(",<(ol)leH>,|words")
966 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
967 QTest::newRow(",<()>olleH|words")
968 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
969 QTest::newRow("<()>,olleH|words")
970 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
971 QTest::newRow("<dlrow( )>,olleH|words,ltr")
972 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
973 QTest::newRow("dlrow<( )>,olleH|words,rtl")
974 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 7 << false;
975 QTest::newRow("<(dlrow )>,olleH|words")
976 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << true;
977 QTest::newRow("<(!dlrow )>,olleH|words")
978 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << true;
979 QTest::newRow("<(!dlrow ,)>olleH|words,ltr")
980 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << false;
981 QTest::newRow("<(!dlrow ,)olleH>|words,rtl")
982 << standard[3] << 8 << 0 << QQuickTextInput::SelectWords << 0 << 13 << false;
983 QTest::newRow("<(!)>dlrow|words,ltr")
984 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
985 QTest::newRow("<(!)dlrow|words,rtl")
986 << standard[3] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 6 << false;
987 QTest::newRow("<()>!dlrow|words")
988 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
989 QTest::newRow("!<()>dlrow|words")
990 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
992 QTest::newRow(" <s(pac)ey> text |words")
993 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
994 QTest::newRow(" spacey <t(ex)t> |words")
995 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << true;
996 QTest::newRow("<( )>spacey text |words|ltr")
997 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
998 QTest::newRow("<( )spacey> text |words|rtl")
999 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
1000 QTest::newRow("spacey <text( )>|words|ltr")
1001 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
1002 QTest::newRow("spacey text<( )>|words|rtl")
1003 << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
1004 QTest::newRow("<()> spacey text |words")
1005 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
1006 QTest::newRow(" spacey text <()>|words")
1007 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
1010 void tst_qquicktextinput::moveCursorSelection()
1012 QFETCH(QString, testStr);
1013 QFETCH(int, cursorPosition);
1014 QFETCH(int, movePosition);
1015 QFETCH(QQuickTextInput::SelectionMode, mode);
1016 QFETCH(int, selectionStart);
1017 QFETCH(int, selectionEnd);
1018 QFETCH(bool, reversible);
1020 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1021 QQmlComponent textinputComponent(&engine);
1022 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1023 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1024 QVERIFY(textinputObject != 0);
1026 textinputObject->setCursorPosition(cursorPosition);
1027 textinputObject->moveCursorSelection(movePosition, mode);
1029 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1030 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1031 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1034 textinputObject->setCursorPosition(movePosition);
1035 textinputObject->moveCursorSelection(cursorPosition, mode);
1037 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1038 QCOMPARE(textinputObject->selectionStart(), selectionStart);
1039 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
1042 delete textinputObject;
1045 void tst_qquicktextinput::moveCursorSelectionSequence_data()
1047 QTest::addColumn<QString>("testStr");
1048 QTest::addColumn<int>("cursorPosition");
1049 QTest::addColumn<int>("movePosition1");
1050 QTest::addColumn<int>("movePosition2");
1051 QTest::addColumn<int>("selection1Start");
1052 QTest::addColumn<int>("selection1End");
1053 QTest::addColumn<int>("selection2Start");
1054 QTest::addColumn<int>("selection2End");
1056 // () contains the text selected by the cursor.
1057 // <> contains the actual selection.
1058 // ^ is the revised cursor position.
1059 // {} contains the revised selection.
1061 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1066 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1071 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1076 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1081 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1086 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1091 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1096 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1101 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1106 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1111 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1116 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1121 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1126 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1131 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1136 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1141 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1146 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1152 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1157 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1163 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1168 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1174 QTest::newRow("{<(^} sp)acey> text |ltr")
1179 QTest::newRow("{<( ^}sp)acey> text |ltr")
1184 QTest::newRow("<( {s^p)acey>} text |rtl")
1189 QTest::newRow("<( {^sp)acey>} text |rtl")
1195 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1200 QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1205 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1210 QTest::newRow(" spacey {<te(xt^} )>|ltr")
1217 void tst_qquicktextinput::moveCursorSelectionSequence()
1219 QFETCH(QString, testStr);
1220 QFETCH(int, cursorPosition);
1221 QFETCH(int, movePosition1);
1222 QFETCH(int, movePosition2);
1223 QFETCH(int, selection1Start);
1224 QFETCH(int, selection1End);
1225 QFETCH(int, selection2Start);
1226 QFETCH(int, selection2End);
1228 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1229 QQmlComponent textinputComponent(&engine);
1230 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1231 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1232 QVERIFY(textinputObject != 0);
1234 textinputObject->setCursorPosition(cursorPosition);
1236 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1237 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1238 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1239 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1241 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1242 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1243 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1244 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1246 delete textinputObject;
1249 void tst_qquicktextinput::dragMouseSelection()
1251 QString qmlfile = testFile("mouseselection_true.qml");
1253 QQuickView window(QUrl::fromLocalFile(qmlfile));
1256 window.requestActivateWindow();
1257 QTest::qWaitForWindowActive(&window);
1259 QVERIFY(window.rootObject() != 0);
1260 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1261 QVERIFY(textInputObject != 0);
1263 // press-and-drag-and-release from x1 to x2
1266 int y = textInputObject->height()/2;
1267 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1268 QTest::mouseMove(&window, QPoint(x2, y));
1269 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1272 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1273 QVERIFY(str1.length() > 3);
1275 // press and drag the current selection.
1278 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1279 QTest::mouseMove(&window, QPoint(x2, y));
1280 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1282 QString str2 = textInputObject->selectedText();
1283 QVERIFY(str2.length() > 3);
1285 QVERIFY(str1 != str2);
1288 void tst_qquicktextinput::mouseSelectionMode_data()
1290 QTest::addColumn<QString>("qmlfile");
1291 QTest::addColumn<bool>("selectWords");
1292 QTest::addColumn<bool>("focus");
1293 QTest::addColumn<bool>("focusOnPress");
1296 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1297 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1298 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1299 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1300 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1301 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1302 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1303 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1304 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1307 void tst_qquicktextinput::mouseSelectionMode()
1309 QFETCH(QString, qmlfile);
1310 QFETCH(bool, selectWords);
1311 QFETCH(bool, focus);
1312 QFETCH(bool, focusOnPress);
1314 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1316 QQuickView window(QUrl::fromLocalFile(qmlfile));
1319 window.requestActivateWindow();
1320 QTest::qWaitForWindowActive(&window);
1322 QVERIFY(window.rootObject() != 0);
1323 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1324 QVERIFY(textInputObject != 0);
1326 textInputObject->setFocus(focus);
1327 textInputObject->setFocusOnPress(focusOnPress);
1329 // press-and-drag-and-release from x1 to x2
1332 int y = textInputObject->height()/2;
1333 QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(x1,y));
1334 QTest::mouseMove(&window, QPoint(x2,y)); // doesn't work
1335 QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(x2,y));
1338 QTRY_COMPARE(textInputObject->selectedText(), text);
1340 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1341 QVERIFY(textInputObject->selectedText() != text);
1345 void tst_qquicktextinput::mouseSelectionMode_accessors()
1347 QQmlComponent component(&engine);
1348 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1349 QScopedPointer<QObject> object(component.create());
1350 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1353 QSignalSpy spy(input, SIGNAL(mouseSelectionModeChanged(SelectionMode)));
1355 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1357 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1358 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectWords);
1359 QCOMPARE(spy.count(), 1);
1361 input->setMouseSelectionMode(QQuickTextInput::SelectWords);
1362 QCOMPARE(spy.count(), 1);
1364 input->setMouseSelectionMode(QQuickTextInput::SelectCharacters);
1365 QCOMPARE(input->mouseSelectionMode(), QQuickTextInput::SelectCharacters);
1366 QCOMPARE(spy.count(), 2);
1369 void tst_qquicktextinput::selectByMouse()
1371 QQmlComponent component(&engine);
1372 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1373 QScopedPointer<QObject> object(component.create());
1374 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1377 QSignalSpy spy(input, SIGNAL(selectByMouseChanged(bool)));
1379 QCOMPARE(input->selectByMouse(), false);
1381 input->setSelectByMouse(true);
1382 QCOMPARE(input->selectByMouse(), true);
1383 QCOMPARE(spy.count(), 1);
1384 QCOMPARE(spy.at(0).at(0).toBool(), true);
1386 input->setSelectByMouse(true);
1387 QCOMPARE(spy.count(), 1);
1389 input->setSelectByMouse(false);
1390 QCOMPARE(input->selectByMouse(), false);
1391 QCOMPARE(spy.count(), 2);
1392 QCOMPARE(spy.at(1).at(0).toBool(), false);
1395 void tst_qquicktextinput::renderType()
1397 QQmlComponent component(&engine);
1398 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1399 QScopedPointer<QObject> object(component.create());
1400 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1403 QSignalSpy spy(input, SIGNAL(renderTypeChanged()));
1405 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1407 input->setRenderType(QQuickTextInput::NativeRendering);
1408 QCOMPARE(input->renderType(), QQuickTextInput::NativeRendering);
1409 QCOMPARE(spy.count(), 1);
1411 input->setRenderType(QQuickTextInput::NativeRendering);
1412 QCOMPARE(spy.count(), 1);
1414 input->setRenderType(QQuickTextInput::QtRendering);
1415 QCOMPARE(input->renderType(), QQuickTextInput::QtRendering);
1416 QCOMPARE(spy.count(), 2);
1419 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1421 PlatformInputContext platformInputContext;
1422 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1423 inputMethodPrivate->testContext = &platformInputContext;
1425 QQuickView window(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1426 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1427 QVERIFY(textInput != 0);
1430 const QString rtlText = textInput->text();
1432 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1433 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1435 // implicit alignment should follow the reading direction of RTL text
1436 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1437 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1438 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1439 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1441 // explicitly left aligned
1442 textInput->setHAlign(QQuickTextInput::AlignLeft);
1443 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1444 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1445 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1447 // explicitly right aligned
1448 textInput->setHAlign(QQuickTextInput::AlignRight);
1449 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1450 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1451 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1452 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1454 // explicitly center aligned
1455 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1456 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1457 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1458 QVERIFY(textInput->boundingRect().left() > 0);
1459 QVERIFY(textInput->boundingRect().right() < textInput->width());
1461 // reseted alignment should go back to following the text reading direction
1462 textInput->resetHAlign();
1463 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1464 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1465 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1466 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1468 // mirror the text item
1469 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1471 // mirrored implicit alignment should continue to follow the reading direction of the text
1472 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1473 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1474 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1475 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1477 // explicitly right aligned behaves as left aligned
1478 textInput->setHAlign(QQuickTextInput::AlignRight);
1479 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1480 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1481 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1483 // mirrored explicitly left aligned behaves as right aligned
1484 textInput->setHAlign(QQuickTextInput::AlignLeft);
1485 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1486 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1487 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1488 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1490 // disable mirroring
1491 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1492 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1493 textInput->resetHAlign();
1495 // English text should be implicitly left aligned
1496 textInput->setText("Hello world!");
1497 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1498 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1500 window.requestActivateWindow();
1501 QTest::qWaitForWindowActive(&window);
1502 QVERIFY(textInput->hasActiveFocus());
1504 // If there is no committed text, the preedit text should determine the alignment.
1505 textInput->setText(QString());
1506 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1507 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1508 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1509 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1511 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1512 // redundant as an actual input method may take care of it.
1513 { QInputMethodEvent ev; QGuiApplication::sendEvent(textInput, &ev); }
1515 // empty text with implicit alignment follows the system locale-based
1516 // keyboard input direction from QInputMethod::inputDirection()
1517 textInput->setText("");
1518 platformInputContext.setInputDirection(Qt::LeftToRight);
1519 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1520 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1521 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1523 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1524 platformInputContext.setInputDirection(Qt::RightToLeft);
1525 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1526 QCOMPARE(cursorRectangleSpy.count(), 1);
1527 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1528 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1529 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1531 // set input direction while having content
1532 platformInputContext.setInputDirection(Qt::LeftToRight);
1533 textInput->setText("a");
1534 platformInputContext.setInputDirection(Qt::RightToLeft);
1535 QTest::keyClick(&window, Qt::Key_Backspace);
1536 QVERIFY(textInput->text().isEmpty());
1537 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1538 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1539 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1541 // input direction changed while not having focus
1542 platformInputContext.setInputDirection(Qt::LeftToRight);
1543 textInput->setFocus(false);
1544 platformInputContext.setInputDirection(Qt::RightToLeft);
1545 textInput->setFocus(true);
1546 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1547 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1548 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1550 textInput->setHAlign(QQuickTextInput::AlignRight);
1551 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1552 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1553 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1555 // neutral text should fall back to input direction
1556 textInput->setFocus(true);
1557 textInput->resetHAlign();
1558 textInput->setText(" ()((=<>");
1559 platformInputContext.setInputDirection(Qt::LeftToRight);
1560 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1561 platformInputContext.setInputDirection(Qt::RightToLeft);
1562 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1564 // changing width keeps right aligned cursor on proper position
1565 textInput->setText("");
1566 textInput->setWidth(500);
1567 QVERIFY(textInput->positionToRectangle(0).x() > textInput->width() / 2);
1570 void tst_qquicktextinput::verticalAlignment()
1572 QQuickView window(testFileUrl("horizontalAlignment.qml"));
1573 QQuickTextInput *textInput = window.rootObject()->findChild<QQuickTextInput*>("text");
1574 QVERIFY(textInput != 0);
1577 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1578 QVERIFY(textInput->boundingRect().bottom() < window.height() / 2);
1579 QVERIFY(textInput->cursorRectangle().bottom() < window.height() / 2);
1580 QVERIFY(textInput->positionToRectangle(0).bottom() < window.height() / 2);
1583 textInput->setVAlign(QQuickTextInput::AlignBottom);
1584 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1585 QVERIFY(textInput->boundingRect().top() > window.height() / 2);
1586 QVERIFY(textInput->cursorRectangle().top() > window.height() / 2);
1587 QVERIFY(textInput->positionToRectangle(0).top() > window.height() / 2);
1589 // explicitly center aligned
1590 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1591 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1592 QVERIFY(textInput->boundingRect().top() < window.height() / 2);
1593 QVERIFY(textInput->boundingRect().bottom() > window.height() / 2);
1594 QVERIFY(textInput->cursorRectangle().top() < window.height() / 2);
1595 QVERIFY(textInput->cursorRectangle().bottom() > window.height() / 2);
1596 QVERIFY(textInput->positionToRectangle(0).top() < window.height() / 2);
1597 QVERIFY(textInput->positionToRectangle(0).bottom() > window.height() / 2);
1600 void tst_qquicktextinput::clipRect()
1602 QQmlComponent component(&engine);
1603 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1604 QScopedPointer<QObject> object(component.create());
1605 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1608 QCOMPARE(input->clipRect().x(), qreal(0));
1609 QCOMPARE(input->clipRect().y(), qreal(0));
1610 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1611 QCOMPARE(input->clipRect().height(), input->height());
1613 input->setText("Hello World");
1614 QCOMPARE(input->clipRect().x(), qreal(0));
1615 QCOMPARE(input->clipRect().y(), qreal(0));
1616 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1617 QCOMPARE(input->clipRect().height(), input->height());
1619 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1620 input->setWidth(input->width() / 2);
1621 QCOMPARE(input->clipRect().x(), qreal(0));
1622 QCOMPARE(input->clipRect().y(), qreal(0));
1623 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1624 QCOMPARE(input->clipRect().height(), input->height());
1626 input->setHeight(input->height() * 2);
1627 QCOMPARE(input->clipRect().x(), qreal(0));
1628 QCOMPARE(input->clipRect().y(), qreal(0));
1629 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1630 QCOMPARE(input->clipRect().height(), input->height());
1632 QQmlComponent cursorComponent(&engine);
1633 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1635 input->setCursorDelegate(&cursorComponent);
1636 input->setCursorVisible(true);
1638 // If a cursor delegate is used it's size should determine the excess width.
1639 QCOMPARE(input->clipRect().x(), qreal(0));
1640 QCOMPARE(input->clipRect().y(), qreal(0));
1641 QCOMPARE(input->clipRect().width(), input->width() + 8);
1642 QCOMPARE(input->clipRect().height(), input->height());
1644 // Alignment, auto scroll, wrapping all don't affect the clip rect.
1645 input->setAutoScroll(false);
1646 QCOMPARE(input->clipRect().x(), qreal(0));
1647 QCOMPARE(input->clipRect().y(), qreal(0));
1648 QCOMPARE(input->clipRect().width(), input->width() + 8);
1649 QCOMPARE(input->clipRect().height(), input->height());
1651 input->setHAlign(QQuickTextInput::AlignRight);
1652 QCOMPARE(input->clipRect().x(), qreal(0));
1653 QCOMPARE(input->clipRect().y(), qreal(0));
1654 QCOMPARE(input->clipRect().width(), input->width() + 8);
1655 QCOMPARE(input->clipRect().height(), input->height());
1657 input->setWrapMode(QQuickTextInput::Wrap);
1658 QCOMPARE(input->clipRect().x(), qreal(0));
1659 QCOMPARE(input->clipRect().y(), qreal(0));
1660 QCOMPARE(input->clipRect().width(), input->width() + 8);
1661 QCOMPARE(input->clipRect().height(), input->height());
1663 input->setVAlign(QQuickTextInput::AlignBottom);
1664 QCOMPARE(input->clipRect().x(), qreal(0));
1665 QCOMPARE(input->clipRect().y(), qreal(0));
1666 QCOMPARE(input->clipRect().width(), input->width() + 8);
1667 QCOMPARE(input->clipRect().height(), input->height());
1670 void tst_qquicktextinput::boundingRect()
1672 QQmlComponent component(&engine);
1673 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1674 QScopedPointer<QObject> object(component.create());
1675 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1679 layout.setFont(input->font());
1681 if (!qmlDisableDistanceField()) {
1683 option.setUseDesignMetrics(true);
1684 layout.setTextOption(option);
1686 layout.beginLayout();
1687 QTextLine line = layout.createLine();
1690 QCOMPARE(input->boundingRect().x(), qreal(0));
1691 QCOMPARE(input->boundingRect().y(), qreal(0));
1692 QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1693 QCOMPARE(input->boundingRect().height(), line.height());
1695 input->setText("Hello World");
1697 layout.setText(input->text());
1698 layout.beginLayout();
1699 line = layout.createLine();
1702 QCOMPARE(input->boundingRect().x(), qreal(0));
1703 QCOMPARE(input->boundingRect().y(), qreal(0));
1704 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1705 QCOMPARE(input->boundingRect().height(), line.height());
1707 // the size of the bounding rect shouldn't be bounded by the size of item.
1708 input->setWidth(input->width() / 2);
1709 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1710 QCOMPARE(input->boundingRect().y(), qreal(0));
1711 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1712 QCOMPARE(input->boundingRect().height(), line.height());
1714 input->setHeight(input->height() * 2);
1715 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1716 QCOMPARE(input->boundingRect().y(), qreal(0));
1717 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1718 QCOMPARE(input->boundingRect().height(), line.height());
1720 QQmlComponent cursorComponent(&engine);
1721 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1723 input->setCursorDelegate(&cursorComponent);
1724 input->setCursorVisible(true);
1726 // Don't include the size of a cursor delegate as it has its own bounding rect.
1727 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1728 QCOMPARE(input->boundingRect().y(), qreal(0));
1729 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1730 QCOMPARE(input->boundingRect().height(), line.height());
1732 // Bounding rect left aligned when auto scroll is disabled;
1733 input->setAutoScroll(false);
1734 QCOMPARE(input->boundingRect().x(), qreal(0));
1735 QCOMPARE(input->boundingRect().y(), qreal(0));
1736 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1737 QCOMPARE(input->boundingRect().height(), line.height());
1739 input->setHAlign(QQuickTextInput::AlignRight);
1740 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1741 QCOMPARE(input->boundingRect().y(), qreal(0));
1742 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1743 QCOMPARE(input->boundingRect().height(), line.height());
1745 input->setWrapMode(QQuickTextInput::Wrap);
1746 QCOMPARE(input->boundingRect().right(), input->width());
1747 QCOMPARE(input->boundingRect().y(), qreal(0));
1748 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1749 QVERIFY(input->boundingRect().height() > line.height());
1751 input->setVAlign(QQuickTextInput::AlignBottom);
1752 QCOMPARE(input->boundingRect().right(), input->width());
1753 QCOMPARE(input->boundingRect().bottom(), input->height());
1754 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1755 QVERIFY(input->boundingRect().height() > line.height());
1758 void tst_qquicktextinput::positionAt()
1760 QQuickView window(testFileUrl("positionAt.qml"));
1761 QVERIFY(window.rootObject() != 0);
1763 window.requestActivateWindow();
1764 QTest::qWaitForWindowActive(&window);
1766 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1767 QVERIFY(textinputObject != 0);
1769 // Check autoscrolled...
1771 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1773 QTextLayout layout(textinputObject->text());
1774 layout.setFont(textinputObject->font());
1776 if (!qmlDisableDistanceField()) {
1778 option.setUseDesignMetrics(true);
1779 layout.setTextOption(option);
1781 layout.beginLayout();
1782 QTextLine line = layout.createLine();
1785 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1786 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1787 int textWidth = floor(line.horizontalAdvance());
1789 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1790 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1792 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1793 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1794 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1796 // Check without autoscroll...
1797 textinputObject->setAutoScroll(false);
1798 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1800 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1801 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1803 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1804 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1806 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1807 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1808 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1810 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1811 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1813 QString preeditText = textinputObject->text().mid(0, pos);
1814 textinputObject->setText(textinputObject->text().mid(pos));
1815 textinputObject->setCursorPosition(0);
1817 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1818 QVERIFY(qGuiApp->focusObject());
1819 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1821 // Check all points within the preedit text return the same position.
1822 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1823 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1824 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1826 // Verify positioning returns to normal after the preedit text.
1827 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1828 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1830 { QInputMethodEvent inputEvent;
1831 QVERIFY(qGuiApp->focusObject());
1832 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1835 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1837 const qreal y0 = line.height() / 2;
1838 const qreal y1 = line.height() * 3 / 2;
1840 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1841 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1843 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1844 QVERIFY(newLinePos > pos);
1845 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1848 void tst_qquicktextinput::maxLength()
1850 QQuickView window(testFileUrl("maxLength.qml"));
1851 QVERIFY(window.rootObject() != 0);
1853 window.requestActivateWindow();
1854 QTest::qWaitForWindowActive(&window);
1856 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1857 QVERIFY(textinputObject != 0);
1858 QVERIFY(textinputObject->text().isEmpty());
1859 QVERIFY(textinputObject->maxLength() == 10);
1860 foreach (const QString &str, standard) {
1861 QVERIFY(textinputObject->text().length() <= 10);
1862 textinputObject->setText(str);
1863 QVERIFY(textinputObject->text().length() <= 10);
1866 textinputObject->setText("");
1867 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1868 for (int i=0; i<20; i++) {
1869 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1870 //simulateKey(&window, Qt::Key_A);
1871 QTest::keyPress(&window, Qt::Key_A);
1872 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1877 void tst_qquicktextinput::masks()
1879 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1880 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1881 QQuickView window(testFileUrl("masks.qml"));
1883 window.requestActivateWindow();
1884 QVERIFY(window.rootObject() != 0);
1885 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
1886 QVERIFY(textinputObject != 0);
1887 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1888 QVERIFY(textinputObject->text().length() == 0);
1889 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1890 QCOMPARE(textinputObject->length(), 8);
1891 for (int i=0; i<10; i++) {
1892 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1893 QCOMPARE(textinputObject->length(), 8);
1894 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1895 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1896 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1897 //simulateKey(&window, Qt::Key_A);
1898 QTest::keyPress(&window, Qt::Key_A);
1899 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
1904 void tst_qquicktextinput::validators()
1906 // Note that this test assumes that the validators are working properly
1907 // so you may need to run their tests first. All validators are checked
1908 // here to ensure that their exposure to QML is working.
1910 QLocale::setDefault(QLocale(QStringLiteral("C")));
1912 QQuickView window(testFileUrl("validators.qml"));
1914 window.requestActivateWindow();
1915 QTest::qWaitForWindowActive(&window);
1917 QVERIFY(window.rootObject() != 0);
1919 QLocale defaultLocale;
1920 QLocale enLocale("en");
1921 QLocale deLocale("de_DE");
1923 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("intInput")));
1925 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1927 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1928 QVERIFY(intValidator);
1929 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1930 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1931 intValidator->setLocaleName(enLocale.name());
1932 QCOMPARE(intValidator->localeName(), enLocale.name());
1933 QCOMPARE(intInput->validator()->locale(), enLocale);
1934 intValidator->resetLocaleName();
1935 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1936 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1938 intInput->setFocus(true);
1939 QTRY_VERIFY(intInput->hasActiveFocus());
1940 QCOMPARE(intInput->hasAcceptableInput(), false);
1941 QCOMPARE(intInput->property("acceptable").toBool(), false);
1942 QTest::keyPress(&window, Qt::Key_1);
1943 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
1945 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1946 QCOMPARE(intInput->hasAcceptableInput(), false);
1947 QCOMPARE(intInput->property("acceptable").toBool(), false);
1948 QCOMPARE(intSpy.count(), 0);
1949 QTest::keyPress(&window, Qt::Key_2);
1950 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
1952 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1953 QCOMPARE(intInput->hasAcceptableInput(), false);
1954 QCOMPARE(intInput->property("acceptable").toBool(), false);
1955 QCOMPARE(intSpy.count(), 0);
1956 QTest::keyPress(&window, Qt::Key_Period);
1957 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
1959 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1960 QCOMPARE(intInput->hasAcceptableInput(), false);
1961 QTest::keyPress(&window, Qt::Key_Comma);
1962 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
1964 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1965 QCOMPARE(intInput->hasAcceptableInput(), false);
1966 QTest::keyPress(&window, Qt::Key_Backspace);
1967 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
1969 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1970 QCOMPARE(intInput->hasAcceptableInput(), false);
1971 intValidator->setLocaleName(deLocale.name());
1972 QTest::keyPress(&window, Qt::Key_Period);
1973 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
1975 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1976 QCOMPARE(intInput->hasAcceptableInput(), false);
1977 QTest::keyPress(&window, Qt::Key_Backspace);
1978 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
1980 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1981 QCOMPARE(intInput->hasAcceptableInput(), false);
1982 intValidator->resetLocaleName();
1983 QTest::keyPress(&window, Qt::Key_1);
1984 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
1986 QCOMPARE(intInput->text(), QLatin1String("11"));
1987 QCOMPARE(intInput->hasAcceptableInput(), true);
1988 QCOMPARE(intInput->property("acceptable").toBool(), true);
1989 QCOMPARE(intSpy.count(), 1);
1990 QTest::keyPress(&window, Qt::Key_0);
1991 QTest::keyRelease(&window, Qt::Key_0, Qt::NoModifier ,10);
1993 QCOMPARE(intInput->text(), QLatin1String("11"));
1994 QCOMPARE(intInput->hasAcceptableInput(), true);
1995 QCOMPARE(intInput->property("acceptable").toBool(), true);
1996 QCOMPARE(intSpy.count(), 1);
1998 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("dblInput")));
2000 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
2002 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
2003 QVERIFY(dblValidator);
2004 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2005 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2006 dblValidator->setLocaleName(enLocale.name());
2007 QCOMPARE(dblValidator->localeName(), enLocale.name());
2008 QCOMPARE(dblInput->validator()->locale(), enLocale);
2009 dblValidator->resetLocaleName();
2010 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
2011 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
2013 dblInput->setFocus(true);
2014 QVERIFY(dblInput->hasActiveFocus() == true);
2015 QCOMPARE(dblInput->hasAcceptableInput(), false);
2016 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2017 QTest::keyPress(&window, Qt::Key_1);
2018 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2020 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2021 QCOMPARE(dblInput->hasAcceptableInput(), false);
2022 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2023 QCOMPARE(dblSpy.count(), 0);
2024 QTest::keyPress(&window, Qt::Key_2);
2025 QTest::keyRelease(&window, Qt::Key_2, Qt::NoModifier ,10);
2027 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2028 QCOMPARE(dblInput->hasAcceptableInput(), true);
2029 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2030 QCOMPARE(dblSpy.count(), 1);
2031 QTest::keyPress(&window, Qt::Key_Comma);
2032 QTest::keyRelease(&window, Qt::Key_Comma, Qt::NoModifier ,10);
2034 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2035 QCOMPARE(dblInput->hasAcceptableInput(), true);
2036 QTest::keyPress(&window, Qt::Key_1);
2037 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2039 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2040 QCOMPARE(dblInput->hasAcceptableInput(), true);
2041 dblValidator->setLocaleName(deLocale.name());
2042 QCOMPARE(dblInput->hasAcceptableInput(), true);
2043 QTest::keyPress(&window, Qt::Key_1);
2044 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2046 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2047 QCOMPARE(dblInput->hasAcceptableInput(), true);
2048 QTest::keyPress(&window, Qt::Key_1);
2049 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2051 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
2052 QCOMPARE(dblInput->hasAcceptableInput(), true);
2053 QTest::keyPress(&window, Qt::Key_Backspace);
2054 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2056 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
2057 QCOMPARE(dblInput->hasAcceptableInput(), true);
2058 QTest::keyPress(&window, Qt::Key_Backspace);
2059 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2061 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
2062 QCOMPARE(dblInput->hasAcceptableInput(), true);
2063 QTest::keyPress(&window, Qt::Key_Backspace);
2064 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2066 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2067 QCOMPARE(dblInput->hasAcceptableInput(), true);
2068 dblValidator->resetLocaleName();
2069 QTest::keyPress(&window, Qt::Key_Period);
2070 QTest::keyRelease(&window, Qt::Key_Period, Qt::NoModifier ,10);
2072 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2073 QCOMPARE(dblInput->hasAcceptableInput(), true);
2074 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2075 QCOMPARE(dblSpy.count(), 1);
2076 QTest::keyPress(&window, Qt::Key_1);
2077 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2079 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2080 QCOMPARE(dblInput->hasAcceptableInput(), true);
2081 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2082 QCOMPARE(dblSpy.count(), 1);
2083 QTest::keyPress(&window, Qt::Key_1);
2084 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2086 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2087 QCOMPARE(dblInput->hasAcceptableInput(), true);
2088 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2089 QCOMPARE(dblSpy.count(), 1);
2090 QTest::keyPress(&window, Qt::Key_1);
2091 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2093 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2094 QCOMPARE(dblInput->hasAcceptableInput(), true);
2095 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2096 QCOMPARE(dblSpy.count(), 1);
2098 // Ensure the validator doesn't prevent characters being removed.
2099 dblInput->setValidator(intInput->validator());
2100 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
2101 QCOMPARE(dblInput->hasAcceptableInput(), false);
2102 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2103 QCOMPARE(dblSpy.count(), 2);
2104 QTest::keyPress(&window, Qt::Key_Backspace);
2105 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2107 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2108 QCOMPARE(dblInput->hasAcceptableInput(), false);
2109 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2110 QCOMPARE(dblSpy.count(), 2);
2111 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
2112 QTest::keyPress(&window, Qt::Key_1);
2113 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2115 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
2116 QCOMPARE(dblInput->hasAcceptableInput(), false);
2117 QCOMPARE(dblSpy.count(), 2);
2118 QTest::keyPress(&window, Qt::Key_Backspace);
2119 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2121 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2122 QCOMPARE(dblInput->hasAcceptableInput(), false);
2123 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2124 QCOMPARE(dblSpy.count(), 2);
2125 QTest::keyPress(&window, Qt::Key_Backspace);
2126 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2128 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2129 QCOMPARE(dblInput->hasAcceptableInput(), false);
2130 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2131 QCOMPARE(dblSpy.count(), 2);
2132 QTest::keyPress(&window, Qt::Key_Backspace);
2133 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2135 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2136 QCOMPARE(dblInput->hasAcceptableInput(), false);
2137 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2138 QCOMPARE(dblSpy.count(), 2);
2139 QTest::keyPress(&window, Qt::Key_Backspace);
2140 QTest::keyRelease(&window, Qt::Key_Backspace, Qt::NoModifier ,10);
2142 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2143 QCOMPARE(dblInput->hasAcceptableInput(), false);
2144 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2145 QCOMPARE(dblSpy.count(), 2);
2146 QTest::keyPress(&window, Qt::Key_1);
2147 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2149 QCOMPARE(dblInput->text(), QLatin1String("11"));
2150 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2151 QCOMPARE(dblInput->hasAcceptableInput(), true);
2152 QCOMPARE(dblSpy.count(), 3);
2154 // Changing the validator properties will re-evaluate whether the input is acceptable.
2155 intValidator->setTop(10);
2156 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2157 QCOMPARE(dblInput->hasAcceptableInput(), false);
2158 QCOMPARE(dblSpy.count(), 4);
2159 intValidator->setTop(12);
2160 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2161 QCOMPARE(dblInput->hasAcceptableInput(), true);
2162 QCOMPARE(dblSpy.count(), 5);
2164 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("strInput")));
2166 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2167 strInput->setFocus(true);
2168 QVERIFY(strInput->hasActiveFocus() == true);
2169 QCOMPARE(strInput->hasAcceptableInput(), false);
2170 QCOMPARE(strInput->property("acceptable").toBool(), false);
2171 QTest::keyPress(&window, Qt::Key_1);
2172 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2174 QTRY_COMPARE(strInput->text(), QLatin1String(""));
2175 QCOMPARE(strInput->hasAcceptableInput(), false);
2176 QCOMPARE(strInput->property("acceptable").toBool(), false);
2177 QCOMPARE(strSpy.count(), 0);
2178 QTest::keyPress(&window, Qt::Key_A);
2179 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2181 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2182 QCOMPARE(strInput->hasAcceptableInput(), false);
2183 QCOMPARE(strInput->property("acceptable").toBool(), false);
2184 QCOMPARE(strSpy.count(), 0);
2185 QTest::keyPress(&window, Qt::Key_A);
2186 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2188 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2189 QCOMPARE(strInput->hasAcceptableInput(), true);
2190 QCOMPARE(strInput->property("acceptable").toBool(), true);
2191 QCOMPARE(strSpy.count(), 1);
2192 QTest::keyPress(&window, Qt::Key_A);
2193 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2195 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2196 QCOMPARE(strInput->hasAcceptableInput(), true);
2197 QCOMPARE(strInput->property("acceptable").toBool(), true);
2198 QCOMPARE(strSpy.count(), 1);
2199 QTest::keyPress(&window, Qt::Key_A);
2200 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2202 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2203 QCOMPARE(strInput->hasAcceptableInput(), true);
2204 QCOMPARE(strInput->property("acceptable").toBool(), true);
2205 QCOMPARE(strSpy.count(), 1);
2206 QTest::keyPress(&window, Qt::Key_A);
2207 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2209 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2210 QCOMPARE(strInput->hasAcceptableInput(), true);
2211 QCOMPARE(strInput->property("acceptable").toBool(), true);
2212 QCOMPARE(strSpy.count(), 1);
2214 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("unvalidatedInput")));
2215 QVERIFY(unvalidatedInput);
2216 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2217 unvalidatedInput->setFocus(true);
2218 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2219 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2220 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2221 QTest::keyPress(&window, Qt::Key_1);
2222 QTest::keyRelease(&window, Qt::Key_1, Qt::NoModifier ,10);
2224 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2225 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2226 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2227 QCOMPARE(unvalidatedSpy.count(), 0);
2228 QTest::keyPress(&window, Qt::Key_A);
2229 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
2231 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2232 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2233 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2234 QCOMPARE(unvalidatedSpy.count(), 0);
2237 void tst_qquicktextinput::inputMethods()
2239 QQuickView window(testFileUrl("inputmethods.qml"));
2241 window.requestActivateWindow();
2242 QTest::qWaitForWindowActive(&window);
2244 // test input method hints
2245 QVERIFY(window.rootObject() != 0);
2246 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(window.rootObject());
2247 QVERIFY(input != 0);
2248 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2249 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2250 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2251 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2252 QCOMPARE(inputMethodHintSpy.count(), 1);
2253 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2254 QCOMPARE(inputMethodHintSpy.count(), 1);
2257 QQuickTextInput plainInput;
2258 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2260 input->setFocus(true);
2261 QVERIFY(input->hasActiveFocus() == true);
2262 // test that input method event is committed
2263 QInputMethodEvent event;
2264 event.setCommitString( "My ", -12, 0);
2265 QTRY_COMPARE(qGuiApp->focusObject(), input);
2266 QGuiApplication::sendEvent(input, &event);
2267 QCOMPARE(input->text(), QString("My Hello world!"));
2269 input->setCursorPosition(2);
2270 event.setCommitString("Your", -2, 2);
2271 QGuiApplication::sendEvent(input, &event);
2272 QCOMPARE(input->text(), QString("Your Hello world!"));
2273 QCOMPARE(input->cursorPosition(), 4);
2275 input->setCursorPosition(7);
2276 event.setCommitString("Goodbye", -2, 5);
2277 QGuiApplication::sendEvent(input, &event);
2278 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2279 QCOMPARE(input->cursorPosition(), 12);
2281 input->setCursorPosition(8);
2282 event.setCommitString("Our", -8, 4);
2283 QGuiApplication::sendEvent(input, &event);
2284 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2285 QCOMPARE(input->cursorPosition(), 7);
2287 // input should reset selection even if replacement parameters are out of bounds
2288 input->setText("text");
2289 input->setCursorPosition(0);
2290 input->moveCursorSelection(input->text().length());
2291 event.setCommitString("replacement", -input->text().length(), input->text().length());
2292 QGuiApplication::sendEvent(input, &event);
2293 QCOMPARE(input->selectionStart(), input->selectionEnd());
2295 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2296 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2297 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2299 input->setReadOnly(true);
2300 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2301 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2305 TextInput element should only handle left/right keys until the cursor reaches
2306 the extent of the text, then they should ignore the keys.
2309 void tst_qquicktextinput::navigation()
2311 QQuickView window(testFileUrl("navigation.qml"));
2313 window.requestActivateWindow();
2315 QVERIFY(window.rootObject() != 0);
2317 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2319 QVERIFY(input != 0);
2320 input->setCursorPosition(0);
2321 QTRY_VERIFY(input->hasActiveFocus() == true);
2322 simulateKey(&window, Qt::Key_Left);
2323 QVERIFY(input->hasActiveFocus() == false);
2324 simulateKey(&window, Qt::Key_Right);
2325 QVERIFY(input->hasActiveFocus() == true);
2326 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2327 input->setCursorPosition(input->text().length());
2328 input->select(0,input->text().length());
2329 QVERIFY(input->selectionStart() != input->selectionEnd());
2330 simulateKey(&window, Qt::Key_Right);
2331 QVERIFY(input->selectionStart() == input->selectionEnd());
2332 QVERIFY(input->selectionStart() == input->text().length());
2333 QVERIFY(input->hasActiveFocus() == true);
2334 simulateKey(&window, Qt::Key_Right);
2335 QVERIFY(input->hasActiveFocus() == false);
2336 simulateKey(&window, Qt::Key_Left);
2337 QVERIFY(input->hasActiveFocus() == true);
2339 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2340 input->setCursorPosition(2);
2341 QCOMPARE(input->cursorPosition(),2);
2342 simulateKey(&window, Qt::Key_Up);
2343 QCOMPARE(input->cursorPosition(),2);
2344 simulateKey(&window, Qt::Key_Down);
2345 QCOMPARE(input->cursorPosition(),2);
2347 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2348 input->setText(QString());
2349 QCOMPARE(input->cursorPosition(), 0);
2350 simulateKey(&window, Qt::Key_Right);
2351 QCOMPARE(input->hasActiveFocus(), false);
2352 simulateKey(&window, Qt::Key_Left);
2353 QCOMPARE(input->hasActiveFocus(), true);
2354 simulateKey(&window, Qt::Key_Left);
2355 QCOMPARE(input->hasActiveFocus(), false);
2358 void tst_qquicktextinput::navigation_RTL()
2360 QQuickView window(testFileUrl("navigation.qml"));
2362 window.requestActivateWindow();
2364 QVERIFY(window.rootObject() != 0);
2366 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
2368 QVERIFY(input != 0);
2369 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2370 input->setText(QString::fromUtf16(arabic_str, 11));
2372 input->setCursorPosition(0);
2373 QTRY_VERIFY(input->hasActiveFocus() == true);
2376 simulateKey(&window, Qt::Key_Right);
2377 QVERIFY(input->hasActiveFocus() == false);
2380 simulateKey(&window, Qt::Key_Left);
2381 QVERIFY(input->hasActiveFocus() == true);
2383 input->setCursorPosition(input->text().length());
2384 QVERIFY(input->hasActiveFocus() == true);
2387 simulateKey(&window, Qt::Key_Left);
2388 QVERIFY(input->hasActiveFocus() == false);
2391 simulateKey(&window, Qt::Key_Right);
2392 QVERIFY(input->hasActiveFocus() == true);
2395 #ifndef QT_NO_CLIPBOARD
2396 void tst_qquicktextinput::copyAndPaste()
2398 if (!PlatformQuirks::isClipboardAvailable())
2399 QSKIP("This machine doesn't support the clipboard");
2401 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2402 QQmlComponent textInputComponent(&engine);
2403 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2404 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2405 QVERIFY(textInput != 0);
2408 QCOMPARE(textInput->text().length(), 12);
2409 textInput->select(0, textInput->text().length());
2411 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2412 QCOMPARE(textInput->selectedText().length(), 12);
2413 textInput->setCursorPosition(0);
2414 QVERIFY(textInput->canPaste());
2416 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2417 QCOMPARE(textInput->text().length(), 24);
2420 QVERIFY(textInput->canPaste());
2421 textInput->setReadOnly(true);
2422 QVERIFY(!textInput->canPaste());
2424 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2425 QCOMPARE(textInput->text().length(), 24);
2426 textInput->setReadOnly(false);
2427 QVERIFY(textInput->canPaste());
2429 // cut: no selection
2431 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2434 textInput->setCursorPosition(0);
2435 textInput->selectWord();
2436 QCOMPARE(textInput->selectedText(), QString("Hello"));
2439 textInput->setReadOnly(true);
2441 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2442 textInput->setReadOnly(false);
2444 // select all and cut
2445 textInput->selectAll();
2447 QCOMPARE(textInput->text().length(), 0);
2449 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2450 QCOMPARE(textInput->text().length(), 24);
2453 textInput->setCursorPosition(0);
2454 textInput->selectWord();
2456 // copy: no selection, previous copy retained;
2457 textInput->setCursorPosition(0);
2458 QCOMPARE(textInput->selectedText(), QString());
2460 textInput->setText(QString());
2462 QCOMPARE(textInput->text(), QString("Hello"));
2464 // clear copy buffer
2465 QClipboard *clipboard = QGuiApplication::clipboard();
2468 QVERIFY(!textInput->canPaste());
2470 // test that copy functionality is disabled
2471 // when echo mode is set to hide text/password mode
2474 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2475 textInput->setEchoMode(echoMode);
2476 textInput->setText("My password");
2477 textInput->select(0, textInput->text().length());
2479 if (echoMode == QQuickTextInput::Normal) {
2480 QVERIFY(!clipboard->text().isEmpty());
2481 QCOMPARE(clipboard->text(), QString("My password"));
2484 QVERIFY(clipboard->text().isEmpty());
2493 #ifndef QT_NO_CLIPBOARD
2494 void tst_qquicktextinput::copyAndPasteKeySequence()
2496 if (!PlatformQuirks::isClipboardAvailable())
2497 QSKIP("This machine doesn't support the clipboard");
2499 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2500 QQmlComponent textInputComponent(&engine);
2501 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2502 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2503 QVERIFY(textInput != 0);
2505 QQuickWindow window;
2506 textInput->setParentItem(window.contentItem());
2508 window.requestActivateWindow();
2509 QTest::qWaitForWindowActive(&window);
2512 QVERIFY(textInput->hasActiveFocus());
2513 QCOMPARE(textInput->text().length(), 12);
2514 textInput->select(0, textInput->text().length());
2515 simulateKeys(&window, QKeySequence::Copy);
2516 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2517 QCOMPARE(textInput->selectedText().length(), 12);
2518 textInput->setCursorPosition(0);
2519 QVERIFY(textInput->canPaste());
2520 simulateKeys(&window, QKeySequence::Paste);
2521 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2522 QCOMPARE(textInput->text().length(), 24);
2524 // select all and cut
2525 simulateKeys(&window, QKeySequence::SelectAll);
2526 simulateKeys(&window, QKeySequence::Cut);
2527 QCOMPARE(textInput->text().length(), 0);
2528 simulateKeys(&window, QKeySequence::Paste);
2529 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2530 QCOMPARE(textInput->text().length(), 24);
2532 // clear copy buffer
2533 QClipboard *clipboard = QGuiApplication::clipboard();
2536 QVERIFY(!textInput->canPaste());
2538 // test that copy functionality is disabled
2539 // when echo mode is set to hide text/password mode
2542 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2543 textInput->setEchoMode(echoMode);
2544 textInput->setText("My password");
2545 textInput->select(0, textInput->text().length());
2546 simulateKeys(&window, QKeySequence::Copy);
2547 if (echoMode == QQuickTextInput::Normal) {
2548 QVERIFY(!clipboard->text().isEmpty());
2549 QCOMPARE(clipboard->text(), QString("My password"));
2552 QVERIFY(clipboard->text().isEmpty());
2561 #ifndef QT_NO_CLIPBOARD
2562 void tst_qquicktextinput::canPasteEmpty()
2564 QGuiApplication::clipboard()->clear();
2566 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2567 QQmlComponent textInputComponent(&engine);
2568 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2569 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2570 QVERIFY(textInput != 0);
2572 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2573 QCOMPARE(textInput->canPaste(), cp);
2577 #ifndef QT_NO_CLIPBOARD
2578 void tst_qquicktextinput::canPaste()
2580 QGuiApplication::clipboard()->setText("Some text");
2582 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2583 QQmlComponent textInputComponent(&engine);
2584 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2585 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2586 QVERIFY(textInput != 0);
2588 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2589 QCOMPARE(textInput->canPaste(), cp);
2593 #ifndef QT_NO_CLIPBOARD
2594 void tst_qquicktextinput::middleClickPaste()
2596 if (!PlatformQuirks::isClipboardAvailable())
2597 QSKIP("This machine doesn't support the clipboard");
2599 QQuickView window(testFileUrl("mouseselection_true.qml"));
2602 window.requestActivateWindow();
2603 QTest::qWaitForWindowActive(&window);
2605 QVERIFY(window.rootObject() != 0);
2606 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(window.rootObject());
2607 QVERIFY(textInputObject != 0);
2609 textInputObject->setFocus(true);
2611 QString originalText = textInputObject->text();
2612 QString selectedText = "234567";
2614 // press-and-drag-and-release from x1 to x2
2615 const QPoint p1 = textInputObject->positionToRectangle(2).center().toPoint();
2616 const QPoint p2 = textInputObject->positionToRectangle(8).center().toPoint();
2617 const QPoint p3 = textInputObject->positionToRectangle(1).center().toPoint();
2618 QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1);
2619 QTest::mouseMove(&window, p2);
2620 QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p2);
2621 QTRY_COMPARE(textInputObject->selectedText(), selectedText);
2623 // Middle click pastes the selected text, assuming the platform supports it.
2624 QTest::mouseClick(&window, Qt::MiddleButton, Qt::NoModifier, p3);
2626 // ### This is to prevent double click detection from carrying over to the next test.
2627 QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 10);
2629 if (QGuiApplication::clipboard()->supportsSelection())
2630 QCOMPARE(textInputObject->text().mid(1, selectedText.length()), selectedText);
2632 QCOMPARE(textInputObject->text(), originalText);
2636 void tst_qquicktextinput::passwordCharacter()
2638 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2639 QQmlComponent textInputComponent(&engine);
2640 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2641 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2642 QVERIFY(textInput != 0);
2644 textInput->setPasswordCharacter("X");
2645 qreal implicitWidth = textInput->implicitWidth();
2646 textInput->setPasswordCharacter(".");
2648 // QTBUG-12383 content is updated and redrawn
2649 QVERIFY(textInput->implicitWidth() < implicitWidth);
2654 void tst_qquicktextinput::cursorDelegate_data()
2656 QTest::addColumn<QUrl>("source");
2657 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2658 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2659 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2662 void tst_qquicktextinput::cursorDelegate()
2664 QFETCH(QUrl, source);
2665 QQuickView view(source);
2667 view.requestActivateWindow();
2668 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2669 QVERIFY(textInputObject != 0);
2670 // Delegate is created on demand, and so won't be available immediately. Focus in or
2671 // setCursorVisible(true) will trigger creation.
2672 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2673 QVERIFY(!textInputObject->isCursorVisible());
2674 //Test Delegate gets created
2675 textInputObject->setFocus(true);
2676 QVERIFY(textInputObject->isCursorVisible());
2677 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2678 QVERIFY(delegateObject);
2679 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2680 //Test Delegate gets moved
2681 for (int i=0; i<= textInputObject->text().length(); i++) {
2682 textInputObject->setCursorPosition(i);
2683 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2684 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2686 textInputObject->setCursorPosition(0);
2687 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2688 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2690 // Test delegate gets moved on mouse press.
2691 textInputObject->setSelectByMouse(true);
2692 textInputObject->setCursorPosition(0);
2693 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2694 QTest::qWait(400); //ensure this isn't treated as a double-click
2695 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2697 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2698 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2699 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2701 // Test delegate gets moved on mouse drag
2702 textInputObject->setCursorPosition(0);
2703 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2704 QTest::qWait(400); //ensure this isn't treated as a double-click
2705 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2706 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2707 QGuiApplication::sendEvent(&view, &mv);
2708 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2710 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2711 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2713 textInputObject->setReadOnly(true);
2714 textInputObject->setCursorPosition(0);
2715 QTest::qWait(400); //ensure this isn't treated as a double-click
2716 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2718 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2719 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2720 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2722 textInputObject->setCursorPosition(0);
2723 QTest::qWait(400); //ensure this isn't treated as a double-click
2724 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2726 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2727 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2728 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2730 textInputObject->setCursorPosition(0);
2731 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2732 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2734 textInputObject->setReadOnly(false);
2736 // Delegate moved when text is entered
2737 textInputObject->setText(QString());
2738 for (int i = 0; i < 20; ++i) {
2739 QTest::keyClick(&view, Qt::Key_A);
2740 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2741 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2744 // Delegate moved when text is entered by im.
2745 textInputObject->setText(QString());
2746 for (int i = 0; i < 20; ++i) {
2747 QInputMethodEvent event;
2748 event.setCommitString("w");
2749 QGuiApplication::sendEvent(textInputObject, &event);
2750 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2751 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2753 // Delegate moved when text is removed by im.
2754 for (int i = 19; i > 1; --i) {
2755 QInputMethodEvent event;
2756 event.setCommitString(QString(), -1, 1);
2757 QGuiApplication::sendEvent(textInputObject, &event);
2758 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2759 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2761 { // Delegate moved the text is changed in place by im.
2762 QInputMethodEvent event;
2763 event.setCommitString("i", -1, 1);
2764 QGuiApplication::sendEvent(textInputObject, &event);
2765 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2766 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2769 //Test Delegate gets deleted
2770 textInputObject->setCursorDelegate(0);
2771 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2774 void tst_qquicktextinput::remoteCursorDelegate()
2776 TestHTTPServer server(SERVER_PORT);
2777 server.serveDirectory(dataDirectory());
2781 QQmlComponent component(view.engine(), QUrl(SERVER_ADDR "/RemoteCursor.qml"));
2783 view.rootContext()->setContextProperty("contextDelegate", &component);
2784 view.setSource(testFileUrl("cursorTestRemote.qml"));
2786 view.requestActivateWindow();
2787 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2788 QVERIFY(textInputObject != 0);
2790 // Delegate is created on demand, and so won't be available immediately. Focus in or
2791 // setCursorVisible(true) will trigger creation.
2792 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2793 QVERIFY(!textInputObject->isCursorVisible());
2795 textInputObject->setFocus(true);
2796 QVERIFY(textInputObject->isCursorVisible());
2798 QCOMPARE(component.status(), QQmlComponent::Loading);
2799 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2801 // Wait for component to load.
2802 QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2803 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2806 void tst_qquicktextinput::cursorVisible()
2808 QQuickTextInput input;
2809 input.componentComplete();
2810 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2812 QQuickView view(testFileUrl("cursorVisible.qml"));
2814 view.requestActivateWindow();
2815 QTest::qWaitForWindowActive(&view);
2817 QCOMPARE(input.isCursorVisible(), false);
2819 input.setCursorVisible(true);
2820 QCOMPARE(input.isCursorVisible(), true);
2821 QCOMPARE(spy.count(), 1);
2823 input.setCursorVisible(false);
2824 QCOMPARE(input.isCursorVisible(), false);
2825 QCOMPARE(spy.count(), 2);
2827 input.setFocus(true);
2828 QCOMPARE(input.isCursorVisible(), false);
2829 QCOMPARE(spy.count(), 2);
2831 input.setParentItem(view.rootObject());
2832 QCOMPARE(input.isCursorVisible(), true);
2833 QCOMPARE(spy.count(), 3);
2835 input.setFocus(false);
2836 QCOMPARE(input.isCursorVisible(), false);
2837 QCOMPARE(spy.count(), 4);
2839 input.setFocus(true);
2840 QCOMPARE(input.isCursorVisible(), true);
2841 QCOMPARE(spy.count(), 5);
2843 QQuickView alternateView;
2844 alternateView.show();
2845 alternateView.requestActivateWindow();
2846 QTest::qWaitForWindowActive(&alternateView);
2848 QCOMPARE(input.isCursorVisible(), false);
2849 QCOMPARE(spy.count(), 6);
2851 view.requestActivateWindow();
2852 QTest::qWaitForWindowActive(&view);
2853 QCOMPARE(input.isCursorVisible(), true);
2854 QCOMPARE(spy.count(), 7);
2856 { // Cursor attribute with 0 length hides cursor.
2857 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2858 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2859 QCoreApplication::sendEvent(&input, &ev);
2861 QCOMPARE(input.isCursorVisible(), false);
2862 QCOMPARE(spy.count(), 8);
2864 { // Cursor attribute with non zero length shows cursor.
2865 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2866 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2867 QCoreApplication::sendEvent(&input, &ev);
2869 QCOMPARE(input.isCursorVisible(), true);
2870 QCOMPARE(spy.count(), 9);
2872 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2873 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2874 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2875 QCoreApplication::sendEvent(&input, &ev);
2877 QCOMPARE(input.isCursorVisible(), false);
2878 QCOMPARE(spy.count(), 10);
2880 input.setText("something");
2881 QCOMPARE(input.isCursorVisible(), true);
2882 QCOMPARE(spy.count(), 11);
2884 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2885 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2886 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2887 QCoreApplication::sendEvent(&input, &ev);
2889 QCOMPARE(input.isCursorVisible(), false);
2890 QCOMPARE(spy.count(), 12);
2892 input.setCursorPosition(5);
2893 QCOMPARE(input.isCursorVisible(), true);
2894 QCOMPARE(spy.count(), 13);
2897 void tst_qquicktextinput::cursorRectangle_data()
2899 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2901 QTest::addColumn<QString>("text");
2902 QTest::addColumn<int>("positionAtWidth");
2903 QTest::addColumn<int>("wrapPosition");
2904 QTest::addColumn<QString>("shortText");
2905 QTest::addColumn<bool>("leftToRight");
2907 QTest::newRow("left to right")
2908 << "Hello World!" << 5 << 11
2911 QTest::newRow("right to left")
2912 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2913 << QString::fromUtf16(arabic_str, 3)
2917 void tst_qquicktextinput::cursorRectangle()
2919 QFETCH(QString, text);
2920 QFETCH(int, positionAtWidth);
2921 QFETCH(int, wrapPosition);
2922 QFETCH(QString, shortText);
2923 QFETCH(bool, leftToRight);
2925 QQuickTextInput input;
2926 input.setText(text);
2927 input.componentComplete();
2929 QTextLayout layout(text);
2930 layout.setFont(input.font());
2931 if (!qmlDisableDistanceField()) {
2933 option.setUseDesignMetrics(true);
2934 layout.setTextOption(option);
2936 layout.beginLayout();
2937 QTextLine line = layout.createLine();
2942 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2944 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2945 offset = line.horizontalAdvance() - input.width();
2947 input.setHeight(qCeil(line.height() * 3 / 2));
2951 for (int i = 0; i <= positionAtWidth; ++i) {
2952 input.setCursorPosition(i);
2953 r = input.cursorRectangle();
2955 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2956 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2957 QCOMPARE(input.positionToRectangle(i), r);
2960 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2961 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2963 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2964 input.setCursorPosition(i);
2965 QCOMPARE(r, input.cursorRectangle());
2966 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2967 QCOMPARE(input.positionToRectangle(i), r);
2970 for (int i = text.length() - 2; i >= 0; --i) {
2971 input.setCursorPosition(i);
2972 r = input.cursorRectangle();
2973 QCOMPARE(r.top(), 0.);
2975 QVERIFY(r.right() >= 0);
2977 QVERIFY(r.left() <= input.width());
2979 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2980 QCOMPARE(input.positionToRectangle(i), r);
2983 // Check position with word wrap.
2984 input.setWrapMode(QQuickTextInput::WordWrap);
2985 input.setAutoScroll(false);
2986 for (int i = 0; i < wrapPosition; ++i) {
2987 input.setCursorPosition(i);
2988 r = input.cursorRectangle();
2990 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2991 QCOMPARE(r.top(), 0.);
2992 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2993 QCOMPARE(input.positionToRectangle(i), r);
2996 input.setCursorPosition(wrapPosition);
2997 r = input.cursorRectangle();
2999 QCOMPARE(r.left(), 0.);
3001 QCOMPARE(r.left(), input.width());
3003 QVERIFY(r.top() >= line.height() - 1);
3004 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3005 QCOMPARE(input.positionToRectangle(11), r);
3007 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3008 input.setCursorPosition(i);
3009 r = input.cursorRectangle();
3010 QVERIFY(r.top() >= line.height() - 1);
3011 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3012 QCOMPARE(input.positionToRectangle(i), r);
3015 // Check vertical scrolling with word wrap.
3016 input.setAutoScroll(true);
3017 for (int i = 0; i <= positionAtWidth; ++i) {
3018 input.setCursorPosition(i);
3019 r = input.cursorRectangle();
3021 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
3022 QCOMPARE(r.top(), 0.);
3023 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3024 QCOMPARE(input.positionToRectangle(i), r);
3027 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
3028 // reaches the next non-whitespace character.
3029 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3030 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
3031 input.setCursorPosition(i);
3032 QCOMPARE(r, input.cursorRectangle());
3033 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3034 QCOMPARE(input.positionToRectangle(i), r);
3037 input.setCursorPosition(wrapPosition);
3038 r = input.cursorRectangle();
3040 QCOMPARE(r.left(), 0.);
3042 QCOMPARE(r.left(), input.width());
3044 QVERIFY(r.bottom() >= input.height());
3045 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3046 QCOMPARE(input.positionToRectangle(11), r);
3048 for (int i = wrapPosition + 1; i < text.length(); ++i) {
3049 input.setCursorPosition(i);
3050 r = input.cursorRectangle();
3051 QVERIFY(r.bottom() >= input.height());
3052 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3053 QCOMPARE(input.positionToRectangle(i), r);
3056 for (int i = text.length() - 2; i >= wrapPosition; --i) {
3057 input.setCursorPosition(i);
3058 r = input.cursorRectangle();
3059 QVERIFY(r.bottom() >= input.height());
3060 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3061 QCOMPARE(input.positionToRectangle(i), r);
3064 input.setCursorPosition(wrapPosition - 1);
3065 r = input.cursorRectangle();
3066 QCOMPARE(r.top(), 0.);
3067 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3068 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3069 QCOMPARE(input.positionToRectangle(10), r);
3071 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
3072 input.setCursorPosition(i);
3073 QCOMPARE(r, input.cursorRectangle());
3074 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3075 QCOMPARE(input.positionToRectangle(i), r);
3078 for (int i = positionAtWidth; i >= 0; --i) {
3079 input.setCursorPosition(i);
3080 r = input.cursorRectangle();
3081 QCOMPARE(r.top(), 0.);
3082 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
3083 QCOMPARE(input.positionToRectangle(i), r);
3086 input.setText(shortText);
3087 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
3088 r = input.cursorRectangle();
3089 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
3092 void tst_qquicktextinput::readOnly()
3094 QQuickView window(testFileUrl("readOnly.qml"));
3096 window.requestActivateWindow();
3098 QVERIFY(window.rootObject() != 0);
3100 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3102 QVERIFY(input != 0);
3103 QTRY_VERIFY(input->hasActiveFocus() == true);
3104 QVERIFY(input->isReadOnly() == true);
3105 QString initial = input->text();
3106 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
3107 simulateKey(&window, k);
3108 simulateKey(&window, Qt::Key_Return);
3109 simulateKey(&window, Qt::Key_Space);
3110 simulateKey(&window, Qt::Key_Escape);
3111 QCOMPARE(input->text(), initial);
3113 input->setCursorPosition(3);
3114 input->setReadOnly(false);
3115 QCOMPARE(input->isReadOnly(), false);
3116 QCOMPARE(input->cursorPosition(), input->text().length());
3119 void tst_qquicktextinput::echoMode()
3121 QQuickView window(testFileUrl("echoMode.qml"));
3123 window.requestActivateWindow();
3124 QTest::qWaitForWindowActive(&window);
3126 QVERIFY(window.rootObject() != 0);
3128 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3130 QVERIFY(input != 0);
3131 QTRY_VERIFY(input->hasActiveFocus() == true);
3132 QString initial = input->text();
3133 Qt::InputMethodHints ref;
3134 QCOMPARE(initial, QLatin1String("ABCDefgh"));
3135 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
3136 QCOMPARE(input->displayText(), input->text());
3138 ref &= ~Qt::ImhHiddenText;
3139 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3140 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3141 input->setEchoMode(QQuickTextInput::NoEcho);
3142 QCOMPARE(input->text(), initial);
3143 QCOMPARE(input->displayText(), QLatin1String(""));
3144 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
3146 ref |= Qt::ImhHiddenText;
3147 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3148 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3149 input->setEchoMode(QQuickTextInput::Password);
3151 ref |= Qt::ImhHiddenText;
3152 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3153 QCOMPARE(input->text(), initial);
3154 QCOMPARE(input->displayText(), QLatin1String("********"));
3155 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3156 // clearing input hints do not clear bits set by echo mode
3157 input->setInputMethodHints(Qt::ImhNone);
3158 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3159 input->setPasswordCharacter(QChar('Q'));
3160 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
3161 QCOMPARE(input->text(), initial);
3162 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3163 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
3164 //PasswordEchoOnEdit
3165 ref &= ~Qt::ImhHiddenText;
3166 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
3167 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
3168 QCOMPARE(input->text(), initial);
3169 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
3170 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
3171 QTest::keyPress(&window, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
3172 QTest::keyRelease(&window, Qt::Key_A, Qt::NoModifier ,10);
3173 QCOMPARE(input->text(), QLatin1String("a"));
3174 QCOMPARE(input->displayText(), QLatin1String("a"));
3175 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
3176 input->setFocus(false);
3177 QVERIFY(input->hasActiveFocus() == false);
3178 QCOMPARE(input->displayText(), QLatin1String("Q"));
3179 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
3180 input->setFocus(true);
3181 QVERIFY(input->hasActiveFocus());
3182 QInputMethodEvent inputEvent;
3183 inputEvent.setCommitString(initial);
3184 QGuiApplication::sendEvent(input, &inputEvent);
3185 QCOMPARE(input->text(), initial);
3186 QCOMPARE(input->displayText(), initial);
3187 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
3190 void tst_qquicktextinput::passwordEchoDelay()
3192 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
3194 QSKIP("No mask delay in use");
3195 QQuickView window(testFileUrl("echoMode.qml"));
3197 window.requestActivateWindow();
3198 QTest::qWaitForWindowActive(&window);
3200 QVERIFY(window.rootObject() != 0);
3202 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(window.rootObject()->property("myInput")));
3204 QVERIFY(input->hasActiveFocus());
3206 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3209 QChar fillChar = QLatin1Char('*');
3211 input->setEchoMode(QQuickTextInput::Password);
3212 QCOMPARE(input->displayText(), QString(8, fillChar));
3213 input->setText(QString());
3214 QCOMPARE(input->displayText(), QString());
3216 QTest::keyPress(&window, '0');
3217 QTest::keyPress(&window, '1');
3218 QTest::keyPress(&window, '2');
3219 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
3220 QTest::keyPress(&window, '3');
3221 QTest::keyPress(&window, '4');
3222 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3223 QTest::keyPress(&window, Qt::Key_Backspace);
3224 QCOMPARE(input->displayText(), QString(4, fillChar));
3225 QTest::keyPress(&window, '4');
3226 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3227 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3229 // Verify the last character entered is replaced by the fill character after a delay.
3230 // Also check the cursor position is updated to accomdate a size difference between
3231 // the fill character and the replaced character.
3232 QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged()));
3233 QTest::qWait(maskDelay);
3234 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
3235 QCOMPARE(cursorSpy.count(), 1);
3236 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3238 QTest::keyPress(&window, '5');
3239 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3240 input->setFocus(false);
3241 QVERIFY(!input->hasFocus());
3242 QCOMPARE(input->displayText(), QString(6, fillChar));
3243 input->setFocus(true);
3244 QTRY_VERIFY(input->hasFocus());
3245 QCOMPARE(input->displayText(), QString(6, fillChar));
3246 QTest::keyPress(&window, '6');
3247 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3249 QInputMethodEvent ev;
3250 ev.setCommitString(QLatin1String("7"));
3251 QGuiApplication::sendEvent(input, &ev);
3252 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3254 input->setCursorPosition(3);
3255 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3256 QTest::keyPress(&window, 'a');
3257 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3258 QTest::keyPress(&window, Qt::Key_Backspace);
3259 QCOMPARE(input->displayText(), QString(8, fillChar));
3263 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3265 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3266 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3268 QGuiApplication::sendEvent(view, &press);
3269 QGuiApplication::sendEvent(view, &release);
3273 void tst_qquicktextinput::focusOnPress()
3275 QString componentStr =
3276 "import QtQuick 2.0\n"
3278 "property bool selectOnFocus: false\n"
3279 "width: 100; height: 50\n"
3280 "activeFocusOnPress: true\n"
3281 "text: \"Hello World\"\n"
3282 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3284 QQmlComponent texteditComponent(&engine);
3285 texteditComponent.setData(componentStr.toLatin1(), QUrl());
3286 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3287 QVERIFY(textInputObject != 0);
3288 QCOMPARE(textInputObject->focusOnPress(), true);
3289 QCOMPARE(textInputObject->hasFocus(), false);
3291 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3292 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3293 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3295 textInputObject->setFocusOnPress(true);
3296 QCOMPARE(textInputObject->focusOnPress(), true);
3297 QCOMPARE(activeFocusOnPressSpy.count(), 0);
3299 QQuickWindow window;
3300 window.resize(100, 50);
3301 textInputObject->setParentItem(window.contentItem());
3303 window.requestActivateWindow();
3304 QTest::qWaitForWindowActive(&window);
3306 QCOMPARE(textInputObject->hasFocus(), false);
3307 QCOMPARE(textInputObject->hasActiveFocus(), false);
3309 QPoint centerPoint(window.width()/2, window.height()/2);
3310 Qt::KeyboardModifiers noModifiers = 0;
3311 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3312 QGuiApplication::processEvents();
3313 QCOMPARE(textInputObject->hasFocus(), true);
3314 QCOMPARE(textInputObject->hasActiveFocus(), true);
3315 QCOMPARE(focusSpy.count(), 1);
3316 QCOMPARE(activeFocusSpy.count(), 1);
3317 QCOMPARE(textInputObject->selectedText(), QString());
3318 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3320 textInputObject->setFocusOnPress(false);
3321 QCOMPARE(textInputObject->focusOnPress(), false);
3322 QCOMPARE(activeFocusOnPressSpy.count(), 1);
3324 textInputObject->setFocus(false);
3325 QCOMPARE(textInputObject->hasFocus(), false);
3326 QCOMPARE(textInputObject->hasActiveFocus(), false);
3327 QCOMPARE(focusSpy.count(), 2);
3328 QCOMPARE(activeFocusSpy.count(), 2);
3330 // Wait for double click timeout to expire before clicking again.
3332 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3333 QGuiApplication::processEvents();
3334 QCOMPARE(textInputObject->hasFocus(), false);
3335 QCOMPARE(textInputObject->hasActiveFocus(), false);
3336 QCOMPARE(focusSpy.count(), 2);
3337 QCOMPARE(activeFocusSpy.count(), 2);
3338 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3340 textInputObject->setFocusOnPress(true);
3341 QCOMPARE(textInputObject->focusOnPress(), true);
3342 QCOMPARE(activeFocusOnPressSpy.count(), 2);
3344 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3345 textInputObject->setProperty("selectOnFocus", true);
3348 QTest::mousePress(&window, Qt::LeftButton, noModifiers, centerPoint);
3349 QGuiApplication::processEvents();
3350 QCOMPARE(textInputObject->hasFocus(), true);
3351 QCOMPARE(textInputObject->hasActiveFocus(), true);
3352 QCOMPARE(focusSpy.count(), 3);
3353 QCOMPARE(activeFocusSpy.count(), 3);
3354 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3355 QTest::mouseRelease(&window, Qt::LeftButton, noModifiers, centerPoint);
3358 void tst_qquicktextinput::openInputPanel()
3360 PlatformInputContext platformInputContext;
3361 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3362 inputMethodPrivate->testContext = &platformInputContext;
3364 QQuickView view(testFileUrl("openInputPanel.qml"));
3366 view.requestActivateWindow();
3367 QTest::qWaitForWindowActive(&view);
3369 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3372 // check default values
3373 QVERIFY(input->focusOnPress());
3374 QVERIFY(!input->hasActiveFocus());
3375 QVERIFY(qApp->focusObject() != input);
3376 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3378 // input panel should open on focus
3379 QPoint centerPoint(view.width()/2, view.height()/2);
3380 Qt::KeyboardModifiers noModifiers = 0;
3381 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3382 QGuiApplication::processEvents();
3383 QVERIFY(input->hasActiveFocus());
3384 QCOMPARE(qApp->focusObject(), input);
3385 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3386 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3388 // input panel should be re-opened when pressing already focused TextInput
3389 qApp->inputMethod()->hide();
3390 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3391 QVERIFY(input->hasActiveFocus());
3392 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3393 QGuiApplication::processEvents();
3394 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3395 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3397 // input panel should stay visible if focus is lost to another text inputor
3398 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3399 QQuickTextInput anotherInput;
3400 anotherInput.componentComplete();
3401 anotherInput.setParentItem(view.rootObject());
3402 anotherInput.setFocus(true);
3403 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3404 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3405 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3407 anotherInput.setFocus(false);
3408 QVERIFY(qApp->focusObject() != &anotherInput);
3409 QCOMPARE(view.activeFocusItem(), view.contentItem());
3410 anotherInput.setFocus(true);
3412 qApp->inputMethod()->hide();
3414 // input panel should not be opened if TextInput is read only
3415 input->setReadOnly(true);
3416 input->setFocus(true);
3417 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3418 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3419 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3420 QGuiApplication::processEvents();
3421 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3423 // input panel should not be opened if focusOnPress is set to false
3424 input->setFocusOnPress(false);
3425 input->setFocus(false);
3426 input->setFocus(true);
3427 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3428 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3429 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3430 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3433 class MyTextInput : public QQuickTextInput
3436 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3440 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3443 return QQuickTextInput::updatePaintNode(node, data);
3448 void tst_qquicktextinput::setHAlignClearCache()
3452 input.setText("Hello world");
3453 input.setParentItem(view.contentItem());
3455 view.requestActivateWindow();
3456 QTest::qWaitForWindowActive(&view);
3458 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3460 QTRY_COMPARE(input.nbPaint, 1);
3461 input.setHAlign(QQuickTextInput::AlignRight);
3462 //Changing the alignment should trigger a repaint
3463 QTRY_COMPARE(input.nbPaint, 2);
3466 void tst_qquicktextinput::focusOutClearSelection()
3469 QQuickTextInput input;
3470 QQuickTextInput input2;
3471 input.setText(QLatin1String("Hello world"));
3472 input.setFocus(true);
3473 input2.setParentItem(view.contentItem());
3474 input.setParentItem(view.contentItem());
3475 input.componentComplete();
3476 input2.componentComplete();
3478 view.requestActivateWindow();
3479 QTest::qWaitForWindowActive(&view);
3480 QVERIFY(input.hasActiveFocus());
3482 //The selection should work
3483 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3484 input2.setFocus(true);
3485 QGuiApplication::processEvents();
3486 //The input lost the focus selection should be cleared
3487 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3490 void tst_qquicktextinput::geometrySignals()
3492 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3493 QObject *o = component.create();
3495 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3496 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3500 void tst_qquicktextinput::contentSize()
3502 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3503 QQmlComponent textComponent(&engine);
3504 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3505 QScopedPointer<QObject> object(textComponent.create());
3506 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3508 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3510 textObject->setText("The quick red fox jumped over the lazy brown dog");
3512 QVERIFY(textObject->contentWidth() > textObject->width());
3513 QVERIFY(textObject->contentHeight() < textObject->height());
3514 QCOMPARE(spy.count(), 1);
3516 textObject->setWrapMode(QQuickTextInput::WordWrap);
3517 QVERIFY(textObject->contentWidth() <= textObject->width());
3518 QVERIFY(textObject->contentHeight() > textObject->height());
3519 QCOMPARE(spy.count(), 2);
3521 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3523 QVERIFY(textObject->contentWidth() > textObject->width());
3524 QVERIFY(textObject->contentHeight() > textObject->height());
3525 QCOMPARE(spy.count(), 3);
3527 textObject->setText("The quick red fox jumped over the lazy brown dog");
3528 for (int w = 60; w < 120; ++w) {
3529 textObject->setWidth(w);
3530 QVERIFY(textObject->contentWidth() <= textObject->width());
3531 QVERIFY(textObject->contentHeight() > textObject->height());
3535 static void sendPreeditText(QQuickItem *item, const QString &text, int cursor)
3537 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3538 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3539 QCoreApplication::sendEvent(item, &event);
3542 void tst_qquicktextinput::preeditAutoScroll()
3544 QString preeditText = "califragisiticexpialidocious!";
3546 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3548 view.requestActivateWindow();
3549 QTest::qWaitForWindowActive(&view);
3550 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3552 QVERIFY(input->hasActiveFocus());
3554 input->setWidth(input->implicitWidth());
3556 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3557 int cursorRectangleChanges = 0;
3559 // test the text is scrolled so the preedit is visible.
3560 sendPreeditText(input, preeditText.mid(0, 3), 1);
3561 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3562 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3563 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3565 // test the text is scrolled back when the preedit is removed.
3566 QInputMethodEvent imEvent;
3567 QCoreApplication::sendEvent(input, &imEvent);
3568 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3569 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3570 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3572 QTextLayout layout(preeditText);
3573 layout.setFont(input->font());
3574 if (!qmlDisableDistanceField()) {
3576 option.setUseDesignMetrics(true);
3577 layout.setTextOption(option);
3579 layout.beginLayout();
3580 QTextLine line = layout.createLine();
3583 // test if the preedit is larger than the text input that the
3584 // character preceding the cursor is still visible.
3585 qreal x = input->positionToRectangle(0).x();
3586 for (int i = 0; i < 3; ++i) {
3587 sendPreeditText(input, preeditText, i + 1);
3588 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3589 QVERIFY(input->cursorRectangle().right() >= width - 3);
3590 QVERIFY(input->positionToRectangle(0).x() < x);
3591 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3592 x = input->positionToRectangle(0).x();
3594 for (int i = 1; i >= 0; --i) {
3595 sendPreeditText(input, preeditText, i + 1);
3596 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3597 QVERIFY(input->cursorRectangle().right() >= width - 3);
3598 QVERIFY(input->positionToRectangle(0).x() > x);
3599 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3600 x = input->positionToRectangle(0).x();
3603 // Test incrementing the preedit cursor doesn't cause further
3604 // scrolling when right most text is visible.
3605 sendPreeditText(input, preeditText, preeditText.length() - 3);
3606 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3607 x = input->positionToRectangle(0).x();
3608 for (int i = 2; i >= 0; --i) {
3609 sendPreeditText(input, preeditText, preeditText.length() - i);
3610 QCOMPARE(input->positionToRectangle(0).x(), x);
3611 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3613 for (int i = 1; i < 3; ++i) {
3614 sendPreeditText(input, preeditText, preeditText.length() - i);
3615 QCOMPARE(input->positionToRectangle(0).x(), x);
3616 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3619 // Test disabling auto scroll.
3620 QCoreApplication::sendEvent(input, &imEvent);
3622 input->setAutoScroll(false);
3623 sendPreeditText(input, preeditText.mid(0, 3), 1);
3624 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3625 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3628 void tst_qquicktextinput::preeditCursorRectangle()
3630 QString preeditText = "super";
3632 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3634 view.requestActivateWindow();
3635 QTest::qWaitForWindowActive(&view);
3636 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3638 QVERIFY(input->hasActiveFocus());
3640 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3645 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3646 QCoreApplication::sendEvent(input, &query);
3647 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3649 // Verify that the micro focus rect is positioned the same for position 0 as
3650 // it would be if there was no preedit text.
3651 sendPreeditText(input, preeditText, 0);
3652 QCoreApplication::sendEvent(input, &query);
3653 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3654 QCOMPARE(currentRect, previousRect);
3655 QCOMPARE(input->cursorRectangle(), currentRect);
3656 QCOMPARE(cursor->pos(), currentRect.topLeft());
3658 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3659 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3661 // Verify that the micro focus rect moves to the left as the cursor position
3663 for (int i = 1; i <= 5; ++i) {
3664 sendPreeditText(input, preeditText, i);
3665 QCoreApplication::sendEvent(input, &query);
3666 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3667 QVERIFY(previousRect.left() < currentRect.left());
3668 QCOMPARE(input->cursorRectangle(), currentRect);
3669 QCOMPARE(cursor->pos(), currentRect.topLeft());
3670 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3671 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3672 previousRect = currentRect;
3675 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3676 // but the (non-zero) cursor position is the same.
3679 sendPreeditText(input, "wwwww", 5);
3680 QCoreApplication::sendEvent(input, &query);
3681 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3682 QCOMPARE(input->cursorRectangle(), currentRect);
3683 QCOMPARE(cursor->pos(), currentRect.topLeft());
3684 QCOMPARE(inputSpy.count(), 1);
3685 QCOMPARE(panelSpy.count(), 1);
3687 // Verify that if there is no preedit cursor then the micro focus rect is the
3688 // same as it would be if it were positioned at the end of the preedit text.
3691 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3692 QCoreApplication::sendEvent(input, &imEvent); }
3693 QCoreApplication::sendEvent(input, &query);
3694 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3695 QCOMPARE(currentRect, previousRect);
3696 QCOMPARE(input->cursorRectangle(), currentRect);
3697 QCOMPARE(cursor->pos(), currentRect.topLeft());
3698 QCOMPARE(inputSpy.count(), 1);
3699 QCOMPARE(panelSpy.count(), 1);
3702 void tst_qquicktextinput::inputContextMouseHandler()
3704 PlatformInputContext platformInputContext;
3705 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3706 inputMethodPrivate->testContext = &platformInputContext;
3708 QString text = "supercalifragisiticexpialidocious!";
3709 QQuickView view(testFileUrl("inputContext.qml"));
3710 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3713 input->setFocus(true);
3717 view.requestActivateWindow();
3718 QTest::qWaitForWindowActive(&view);
3720 QTextLayout layout(text);
3721 layout.setFont(input->font());
3722 if (!qmlDisableDistanceField()) {
3724 option.setUseDesignMetrics(true);
3725 layout.setTextOption(option);
3727 layout.beginLayout();
3728 QTextLine line = layout.createLine();
3731 const qreal x = line.cursorToX(2, QTextLine::Leading);
3732 const qreal y = line.height() / 2;
3733 QPoint position = QPointF(x, y).toPoint();
3735 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3736 QGuiApplication::sendEvent(input, &inputEvent);
3738 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3739 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3740 QGuiApplication::processEvents();
3742 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3743 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3744 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3747 void tst_qquicktextinput::inputMethodComposing()
3749 QString text = "supercalifragisiticexpialidocious!";
3751 QQuickView view(testFileUrl("inputContext.qml"));
3753 view.requestActivateWindow();
3754 QTest::qWaitForWindowActive(&view);
3755 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3757 QVERIFY(input->hasActiveFocus());
3758 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3760 QCOMPARE(input->isInputMethodComposing(), false);
3762 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3763 QGuiApplication::sendEvent(input, &event);
3765 QCOMPARE(input->isInputMethodComposing(), true);
3766 QCOMPARE(spy.count(), 1);
3769 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3770 QGuiApplication::sendEvent(input, &event);
3772 QCOMPARE(spy.count(), 1);
3775 QInputMethodEvent event;
3776 QGuiApplication::sendEvent(input, &event);
3778 QCOMPARE(input->isInputMethodComposing(), false);
3779 QCOMPARE(spy.count(), 2);
3781 // Changing the text while not composing doesn't alter the composing state.
3782 input->setText(text.mid(0, 16));
3783 QCOMPARE(input->isInputMethodComposing(), false);
3784 QCOMPARE(spy.count(), 2);
3787 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3788 QGuiApplication::sendEvent(input, &event);
3790 QCOMPARE(input->isInputMethodComposing(), true);
3791 QCOMPARE(spy.count(), 3);
3793 // Changing the text while composing cancels composition.
3794 input->setText(text.mid(0, 12));
3795 QCOMPARE(input->isInputMethodComposing(), false);
3796 QCOMPARE(spy.count(), 4);
3798 { // Preedit cursor positioned outside (empty) preedit; composing.
3799 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3800 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3801 QGuiApplication::sendEvent(input, &event);
3803 QCOMPARE(input->isInputMethodComposing(), true);
3804 QCOMPARE(spy.count(), 5);
3807 { // Cursor hidden; composing
3808 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3809 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3810 QGuiApplication::sendEvent(input, &event);
3812 QCOMPARE(input->isInputMethodComposing(), true);
3813 QCOMPARE(spy.count(), 5);
3815 { // Default cursor attributes; composing.
3816 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3817 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3818 QGuiApplication::sendEvent(input, &event);
3820 QCOMPARE(input->isInputMethodComposing(), true);
3821 QCOMPARE(spy.count(), 5);
3823 { // Selections are persisted: not composing
3824 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3825 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3826 QGuiApplication::sendEvent(input, &event);
3828 QCOMPARE(input->isInputMethodComposing(), false);
3829 QCOMPARE(spy.count(), 6);
3831 input->setCursorPosition(12);
3833 { // Formatting applied; composing.
3834 QTextCharFormat format;
3835 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3836 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3837 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3838 QGuiApplication::sendEvent(input, &event);
3840 QCOMPARE(input->isInputMethodComposing(), true);
3841 QCOMPARE(spy.count(), 7);
3844 QInputMethodEvent event;
3845 QGuiApplication::sendEvent(input, &event);
3847 QCOMPARE(input->isInputMethodComposing(), false);
3848 QCOMPARE(spy.count(), 8);
3851 void tst_qquicktextinput::inputMethodUpdate()
3853 PlatformInputContext platformInputContext;
3854 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3855 inputMethodPrivate->testContext = &platformInputContext;
3857 QQuickView view(testFileUrl("inputContext.qml"));
3859 view.requestActivateWindow();
3860 QTest::qWaitForWindowActive(&view);
3861 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3863 QVERIFY(input->hasActiveFocus());
3865 // text change even without cursor position change needs to trigger update
3866 input->setText("test");
3867 platformInputContext.clear();
3868 input->setText("xxxx");
3869 QVERIFY(platformInputContext.m_updateCallCount > 0);
3871 // input method event replacing text
3872 platformInputContext.clear();
3874 QInputMethodEvent inputMethodEvent;
3875 inputMethodEvent.setCommitString("y", -1, 1);
3876 QGuiApplication::sendEvent(input, &inputMethodEvent);
3878 QVERIFY(platformInputContext.m_updateCallCount > 0);
3880 // input method changing selection
3881 platformInputContext.clear();
3883 QList<QInputMethodEvent::Attribute> attributes;
3884 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3885 QInputMethodEvent inputMethodEvent("", attributes);
3886 QGuiApplication::sendEvent(input, &inputMethodEvent);
3888 QVERIFY(input->selectionStart() != input->selectionEnd());
3889 QVERIFY(platformInputContext.m_updateCallCount > 0);
3891 // programmatical selections trigger update
3892 platformInputContext.clear();
3894 QVERIFY(platformInputContext.m_updateCallCount > 0);
3897 platformInputContext.clear();
3898 QFont font = input->font();
3899 font.setBold(!font.bold());
3900 input->setFont(font);
3901 QVERIFY(platformInputContext.m_updateCallCount > 0);
3904 platformInputContext.clear();
3906 QInputMethodEvent inputMethodEvent;
3907 inputMethodEvent.setCommitString("y");
3908 QGuiApplication::sendEvent(input, &inputMethodEvent);
3910 QVERIFY(platformInputContext.m_updateCallCount > 0);
3912 // changing cursor position
3913 platformInputContext.clear();
3914 input->setCursorPosition(0);
3915 QVERIFY(platformInputContext.m_updateCallCount > 0);
3917 // read only disabled input method
3918 platformInputContext.clear();
3919 input->setReadOnly(true);
3920 QVERIFY(platformInputContext.m_updateCallCount > 0);
3921 input->setReadOnly(false);
3923 // no updates while no focus
3924 input->setFocus(false);
3925 platformInputContext.clear();
3926 input->setText("Foo");
3927 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3928 input->setCursorPosition(1);
3929 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3931 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3932 input->setReadOnly(true);
3933 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3936 void tst_qquicktextinput::cursorRectangleSize()
3938 QQuickView *window = new QQuickView(testFileUrl("positionAt.qml"));
3939 QVERIFY(window->rootObject() != 0);
3940 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(window->rootObject());
3942 // make sure cursor rectangle is not at (0,0)
3943 textInput->setX(10);
3944 textInput->setY(10);
3945 textInput->setCursorPosition(3);
3946 QVERIFY(textInput != 0);
3947 textInput->setFocus(true);
3949 window->requestActivateWindow();
3950 QTest::qWaitForWindowActive(window);
3951 QVERIFY(textInput->hasActiveFocus());
3953 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3954 qApp->sendEvent(textInput, &event);
3955 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3957 QRectF cursorRectFromItem = textInput->cursorRectangle();
3958 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3960 // item and input query cursor rectangles match
3961 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3963 // item cursor rectangle and positionToRectangle calculations match
3964 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3966 // item-window transform and input item transform match
3967 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToWindowTransform(), qApp->inputMethod()->inputItemTransform());
3969 // input panel cursorRectangle property and tranformed item cursor rectangle match
3970 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToWindowTransform().mapRect(cursorRectFromItem);
3971 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3976 void tst_qquicktextinput::tripleClickSelectsAll()
3978 QString qmlfile = testFile("positionAt.qml");
3979 QQuickView view(QUrl::fromLocalFile(qmlfile));
3981 view.requestActivateWindow();
3982 QTest::qWaitForWindowActive(&view);
3984 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3987 QLatin1String hello("Hello world!");
3988 input->setSelectByMouse(true);
3989 input->setText(hello);
3991 // Clicking on the same point inside TextInput three times in a row
3992 // should trigger a triple click, thus selecting all the text.
3993 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3994 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3995 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3996 QGuiApplication::processEvents();
3997 QCOMPARE(input->selectedText(), hello);
3999 // Now it simulates user moving the mouse between the second and the third click.
4000 // In this situation, we don't expect a triple click.
4001 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
4002 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4003 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
4004 QGuiApplication::processEvents();
4005 QVERIFY(input->selectedText().isEmpty());
4007 // And now we press the third click too late, so no triple click event is triggered.
4008 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
4009 QGuiApplication::processEvents();
4010 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
4011 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
4012 QGuiApplication::processEvents();
4013 QVERIFY(input->selectedText().isEmpty());
4016 void tst_qquicktextinput::QTBUG_19956_data()
4018 QTest::addColumn<QString>("url");
4019 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
4020 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
4024 void tst_qquicktextinput::getText_data()
4026 QTest::addColumn<QString>("text");
4027 QTest::addColumn<QString>("inputMask");
4028 QTest::addColumn<int>("start");
4029 QTest::addColumn<int>("end");
4030 QTest::addColumn<QString>("expectedText");
4032 QTest::newRow("all plain text")
4035 << 0 << standard.at(0).length()
4038 QTest::newRow("plain text sub string")
4042 << standard.at(0).mid(0, 12);
4044 QTest::newRow("plain text sub string reversed")
4048 << standard.at(0).mid(0, 12);
4050 QTest::newRow("plain text cropped beginning")
4054 << standard.at(0).mid(0, 4);
4056 QTest::newRow("plain text cropped end")
4059 << 23 << standard.at(0).length() + 8
4060 << standard.at(0).mid(23);
4062 QTest::newRow("plain text cropped beginning and end")
4065 << -9 << standard.at(0).length() + 4
4069 void tst_qquicktextinput::getText()
4071 QFETCH(QString, text);
4072 QFETCH(QString, inputMask);
4075 QFETCH(QString, expectedText);
4077 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4078 QQmlComponent textInputComponent(&engine);
4079 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4080 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4081 QVERIFY(textInput != 0);
4083 QCOMPARE(textInput->getText(start, end), expectedText);
4086 void tst_qquicktextinput::insert_data()
4088 QTest::addColumn<QString>("text");
4089 QTest::addColumn<QString>("inputMask");
4090 QTest::addColumn<int>("selectionStart");
4091 QTest::addColumn<int>("selectionEnd");
4092 QTest::addColumn<int>("insertPosition");
4093 QTest::addColumn<QString>("insertText");
4094 QTest::addColumn<QString>("expectedText");
4095 QTest::addColumn<int>("expectedSelectionStart");
4096 QTest::addColumn<int>("expectedSelectionEnd");
4097 QTest::addColumn<int>("expectedCursorPosition");
4098 QTest::addColumn<bool>("selectionChanged");
4099 QTest::addColumn<bool>("cursorPositionChanged");
4101 QTest::newRow("at cursor position (beginning)")
4106 << QString("Hello") + standard.at(0)
4110 QTest::newRow("at cursor position (end)")
4113 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
4115 << standard.at(0) + QString("Hello")
4116 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4119 QTest::newRow("at cursor position (middle)")
4124 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4128 QTest::newRow("after cursor position (beginning)")
4133 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4137 QTest::newRow("before cursor position (end)")
4140 << standard.at(0).length() << standard.at(0).length() << 18
4142 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4143 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
4146 QTest::newRow("before cursor position (middle)")
4151 << QString("Hello") + standard.at(0)
4155 QTest::newRow("after cursor position (middle)")
4158 << 18 << 18 << standard.at(0).length()
4160 << standard.at(0) + QString("Hello")
4164 QTest::newRow("before selection")
4169 << QString("Hello") + standard.at(0)
4173 QTest::newRow("before reversed selection")
4178 << QString("Hello") + standard.at(0)
4182 QTest::newRow("after selection")
4185 << 14 << 19 << standard.at(0).length()
4187 << standard.at(0) + QString("Hello")
4191 QTest::newRow("after reversed selection")
4194 << 19 << 14 << standard.at(0).length()
4196 << standard.at(0) + QString("Hello")
4200 QTest::newRow("into selection")
4205 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4209 QTest::newRow("into reversed selection")
4214 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4218 QTest::newRow("rich text into plain text")
4222 << QString("<b>Hello</b>")
4223 << QString("<b>Hello</b>") + standard.at(0)
4227 QTest::newRow("before start")
4236 QTest::newRow("past end")
4239 << 0 << 0 << standard.at(0).length() + 3
4245 const QString inputMask = "009.009.009.009";
4246 const QString ip = "192.168.2.14";
4248 QTest::newRow("mask: at cursor position (beginning)")
4253 << QString("125.168.2.14")
4257 QTest::newRow("mask: at cursor position (end)")
4260 << inputMask.length() << inputMask.length() << inputMask.length()
4263 << inputMask.length() << inputMask.length() << inputMask.length()
4266 QTest::newRow("mask: at cursor position (middle)")
4271 << QString("192.167.5.24")
4275 QTest::newRow("mask: after cursor position (beginning)")
4280 << QString("192.167.5.24")
4284 QTest::newRow("mask: before cursor position (end)")
4287 << inputMask.length() << inputMask.length() << 6
4289 << QString("192.167.5.24")
4290 << inputMask.length() << inputMask.length() << inputMask.length()
4293 QTest::newRow("mask: before cursor position (middle)")
4298 << QString("125.168.2.14")
4302 QTest::newRow("mask: after cursor position (middle)")
4311 QTest::newRow("mask: before selection")
4316 << QString("125.168.2.14")
4320 QTest::newRow("mask: before reversed selection")
4325 << QString("125.168.2.14")
4329 QTest::newRow("mask: after selection")
4338 QTest::newRow("mask: after reversed selection")
4347 QTest::newRow("mask: into selection")
4352 << QString("192.167.5.24")
4356 QTest::newRow("mask: into reversed selection")
4361 << QString("192.167.5.24")
4365 QTest::newRow("mask: before start")
4374 QTest::newRow("mask: past end")
4377 << 0 << 0 << ip.length() + 3
4383 QTest::newRow("mask: invalid characters")
4388 << QString("192.168.2.14")
4392 QTest::newRow("mask: mixed validity")
4396 << QString("a1b2c5")
4397 << QString("125.168.2.14")
4402 void tst_qquicktextinput::insert()
4404 QFETCH(QString, text);
4405 QFETCH(QString, inputMask);
4406 QFETCH(int, selectionStart);
4407 QFETCH(int, selectionEnd);
4408 QFETCH(int, insertPosition);
4409 QFETCH(QString, insertText);
4410 QFETCH(QString, expectedText);
4411 QFETCH(int, expectedSelectionStart);
4412 QFETCH(int, expectedSelectionEnd);
4413 QFETCH(int, expectedCursorPosition);
4414 QFETCH(bool, selectionChanged);
4415 QFETCH(bool, cursorPositionChanged);
4417 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4418 QQmlComponent textInputComponent(&engine);
4419 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4420 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4421 QVERIFY(textInput != 0);
4423 textInput->select(selectionStart, selectionEnd);
4425 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4426 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4427 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4428 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4429 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4431 textInput->insert(insertPosition, insertText);
4433 QCOMPARE(textInput->text(), expectedText);
4434 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4436 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4437 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4438 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4440 if (selectionStart > selectionEnd)
4441 qSwap(selectionStart, selectionEnd);
4443 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4444 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4445 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4446 QCOMPARE(textSpy.count() > 0, text != expectedText);
4447 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4450 void tst_qquicktextinput::remove_data()
4452 QTest::addColumn<QString>("text");
4453 QTest::addColumn<QString>("inputMask");
4454 QTest::addColumn<int>("selectionStart");
4455 QTest::addColumn<int>("selectionEnd");
4456 QTest::addColumn<int>("removeStart");
4457 QTest::addColumn<int>("removeEnd");
4458 QTest::addColumn<QString>("expectedText");
4459 QTest::addColumn<int>("expectedSelectionStart");
4460 QTest::addColumn<int>("expectedSelectionEnd");
4461 QTest::addColumn<int>("expectedCursorPosition");
4462 QTest::addColumn<bool>("selectionChanged");
4463 QTest::addColumn<bool>("cursorPositionChanged");
4465 QTest::newRow("from cursor position (beginning)")
4470 << standard.at(0).mid(5)
4474 QTest::newRow("to cursor position (beginning)")
4479 << standard.at(0).mid(5)
4483 QTest::newRow("to cursor position (end)")
4486 << standard.at(0).length() << standard.at(0).length()
4487 << standard.at(0).length() << standard.at(0).length() - 5
4488 << standard.at(0).mid(0, standard.at(0).length() - 5)
4489 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4492 QTest::newRow("to cursor position (end)")
4495 << standard.at(0).length() << standard.at(0).length()
4496 << standard.at(0).length() - 5 << standard.at(0).length()
4497 << standard.at(0).mid(0, standard.at(0).length() - 5)
4498 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4501 QTest::newRow("from cursor position (middle)")
4506 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4510 QTest::newRow("to cursor position (middle)")
4515 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4519 QTest::newRow("after cursor position (beginning)")
4524 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4528 QTest::newRow("before cursor position (end)")
4531 << standard.at(0).length() << standard.at(0).length()
4533 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4534 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4537 QTest::newRow("before cursor position (middle)")
4542 << standard.at(0).mid(5)
4546 QTest::newRow("after cursor position (middle)")
4551 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4555 QTest::newRow("before selection")
4560 << standard.at(0).mid(5)
4564 QTest::newRow("before reversed selection")
4569 << standard.at(0).mid(5)
4573 QTest::newRow("after selection")
4577 << standard.at(0).length() - 5 << standard.at(0).length()
4578 << standard.at(0).mid(0, standard.at(0).length() - 5)
4582 QTest::newRow("after reversed selection")
4586 << standard.at(0).length() - 5 << standard.at(0).length()
4587 << standard.at(0).mid(0, standard.at(0).length() - 5)
4591 QTest::newRow("from selection")
4596 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4600 QTest::newRow("from reversed selection")
4605 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4609 QTest::newRow("cropped beginning")
4614 << standard.at(0).mid(4)
4618 QTest::newRow("cropped end")
4622 << 23 << standard.at(0).length() + 8
4623 << standard.at(0).mid(0, 23)
4627 QTest::newRow("cropped beginning and end")
4631 << -9 << standard.at(0).length() + 4
4636 const QString inputMask = "009.009.009.009";
4637 const QString ip = "192.168.2.14";
4639 QTest::newRow("mask: from cursor position")
4644 << QString("192.16..14")
4648 QTest::newRow("mask: to cursor position")
4653 << QString("19.8.2.14")
4657 QTest::newRow("mask: before cursor position")
4662 << QString("2.168.2.14")
4666 QTest::newRow("mask: after cursor position")
4671 << QString("192.168.2.")
4675 QTest::newRow("mask: before selection")
4680 << QString("2.168.2.14")
4684 QTest::newRow("mask: before reversed selection")
4689 << QString("2.168.2.14")
4693 QTest::newRow("mask: after selection")
4698 << QString("192.168.2.")
4702 QTest::newRow("mask: after reversed selection")
4707 << QString("192.168.2.")
4711 QTest::newRow("mask: from selection")
4716 << QString("192.168..14")
4720 QTest::newRow("mask: from reversed selection")
4725 << QString("192.168..14")
4729 QTest::newRow("mask: cropped beginning")
4734 << QString(".168.2.14")
4738 QTest::newRow("mask: cropped end")
4743 << QString("192.168.2.1")
4747 QTest::newRow("mask: cropped beginning and end")
4757 void tst_qquicktextinput::remove()
4759 QFETCH(QString, text);
4760 QFETCH(QString, inputMask);
4761 QFETCH(int, selectionStart);
4762 QFETCH(int, selectionEnd);
4763 QFETCH(int, removeStart);
4764 QFETCH(int, removeEnd);
4765 QFETCH(QString, expectedText);
4766 QFETCH(int, expectedSelectionStart);
4767 QFETCH(int, expectedSelectionEnd);
4768 QFETCH(int, expectedCursorPosition);
4769 QFETCH(bool, selectionChanged);
4770 QFETCH(bool, cursorPositionChanged);
4772 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4773 QQmlComponent textInputComponent(&engine);
4774 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4775 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4776 QVERIFY(textInput != 0);
4778 textInput->select(selectionStart, selectionEnd);
4780 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4781 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4782 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4783 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4784 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4786 textInput->remove(removeStart, removeEnd);
4788 QCOMPARE(textInput->text(), expectedText);
4789 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4791 if (selectionStart > selectionEnd) //
4792 qSwap(selectionStart, selectionEnd);
4794 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4795 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4796 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4798 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4799 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4800 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4801 QCOMPARE(textSpy.count() > 0, text != expectedText);
4803 if (cursorPositionChanged) //
4804 QVERIFY(cursorPositionSpy.count() > 0);
4807 void tst_qquicktextinput::keySequence_data()
4809 QTest::addColumn<QString>("text");
4810 QTest::addColumn<QKeySequence>("sequence");
4811 QTest::addColumn<int>("selectionStart");
4812 QTest::addColumn<int>("selectionEnd");
4813 QTest::addColumn<int>("cursorPosition");
4814 QTest::addColumn<QString>("expectedText");
4815 QTest::addColumn<QString>("selectedText");
4816 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4817 QTest::addColumn<Qt::Key>("layoutDirection");
4819 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4821 QTest::newRow("select all")
4822 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4823 << 44 << standard.at(0) << standard.at(0)
4824 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4825 QTest::newRow("select start of line")
4826 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4827 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4828 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4829 QTest::newRow("select start of block")
4830 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4831 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4832 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4833 QTest::newRow("select end of line")
4834 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4835 << 44 << standard.at(0) << standard.at(0).mid(5)
4836 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4837 QTest::newRow("select end of document") // ### Not handled.
4838 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4839 << 3 << standard.at(0) << QString()
4840 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4841 QTest::newRow("select end of block")
4842 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4843 << 44 << standard.at(0) << standard.at(0).mid(18)
4844 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4845 QTest::newRow("delete end of line")
4846 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4847 << 24 << standard.at(0).mid(0, 24) << QString()
4848 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4849 QTest::newRow("move to start of line")
4850 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4851 << 0 << standard.at(0) << QString()
4852 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4853 QTest::newRow("move to start of block")
4854 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4855 << 0 << standard.at(0) << QString()
4856 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4857 QTest::newRow("move to next char")
4858 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4859 << 13 << standard.at(0) << QString()
4860 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4861 QTest::newRow("move to previous char (ltr)")
4862 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4863 << 2 << standard.at(0) << QString()
4864 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4865 QTest::newRow("move to previous char (rtl)")
4866 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4867 << 4 << standard.at(0) << QString()
4868 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4869 QTest::newRow("move to previous char with selection")
4870 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4871 << 3 << standard.at(0) << QString()
4872 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4873 QTest::newRow("select next char (ltr)")
4874 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4875 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4876 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4877 QTest::newRow("select next char (rtl)")
4878 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4879 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4880 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4881 QTest::newRow("select previous char (ltr)")
4882 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4883 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4884 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4885 QTest::newRow("select previous char (rtl)")
4886 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4887 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4888 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4889 QTest::newRow("move to next word (ltr)")
4890 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4891 << 10 << standard.at(0) << QString()
4892 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4893 QTest::newRow("move to next word (rtl)")
4894 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4895 << 4 << standard.at(0) << QString()
4896 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4897 QTest::newRow("move to next word (password,ltr)")
4898 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4899 << 44 << standard.at(0) << QString()
4900 << QQuickTextInput::Password << Qt::Key_Direction_L;
4901 QTest::newRow("move to next word (password,rtl)")
4902 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4903 << 0 << standard.at(0) << QString()
4904 << QQuickTextInput::Password << Qt::Key_Direction_R;
4905 QTest::newRow("move to previous word (ltr)")
4906 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4907 << 4 << standard.at(0) << QString()
4908 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4909 QTest::newRow("move to previous word (rlt)")
4910 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4911 << 10 << standard.at(0) << QString()
4912 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4913 QTest::newRow("move to previous word (password,ltr)")
4914 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4915 << 0 << standard.at(0) << QString()
4916 << QQuickTextInput::Password << Qt::Key_Direction_L;
4917 QTest::newRow("move to previous word (password,rtl)")
4918 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4919 << 44 << standard.at(0) << QString()
4920 << QQuickTextInput::Password << Qt::Key_Direction_R;
4921 QTest::newRow("select next word")
4922 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4923 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4924 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4925 QTest::newRow("select previous word")
4926 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4927 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4928 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4929 QTest::newRow("delete (selection)")
4930 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4931 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4932 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4933 QTest::newRow("delete (no selection)")
4934 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4935 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4936 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4937 QTest::newRow("delete end of word")
4938 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4939 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4940 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4941 QTest::newRow("delete start of word")
4942 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4943 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4944 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4947 void tst_qquicktextinput::keySequence()
4949 QFETCH(QString, text);
4950 QFETCH(QKeySequence, sequence);
4951 QFETCH(int, selectionStart);
4952 QFETCH(int, selectionEnd);
4953 QFETCH(int, cursorPosition);
4954 QFETCH(QString, expectedText);
4955 QFETCH(QString, selectedText);
4956 QFETCH(QQuickTextInput::EchoMode, echoMode);
4957 QFETCH(Qt::Key, layoutDirection);
4959 if (sequence.isEmpty()) {
4960 QSKIP("Key sequence is undefined");
4963 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4964 QQmlComponent textInputComponent(&engine);
4965 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4966 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4967 QVERIFY(textInput != 0);
4968 textInput->setEchoMode(echoMode);
4970 QQuickWindow window;
4971 textInput->setParentItem(window.contentItem());
4973 window.requestActivateWindow();
4974 QTest::qWaitForWindowActive(&window);
4975 QVERIFY(textInput->hasActiveFocus());
4977 simulateKey(&window, layoutDirection);
4979 textInput->select(selectionStart, selectionEnd);
4981 simulateKeys(&window, sequence);
4983 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4984 QCOMPARE(textInput->text(), expectedText);
4985 QCOMPARE(textInput->selectedText(), selectedText);
4989 #define REPLACE_UNTIL_END 1
4991 void tst_qquicktextinput::undo_data()
4993 QTest::addColumn<QStringList>("insertString");
4994 QTest::addColumn<IntList>("insertIndex");
4995 QTest::addColumn<IntList>("insertMode");
4996 QTest::addColumn<QStringList>("expectedString");
4997 QTest::addColumn<bool>("use_keys");
4999 for (int i=0; i<2; i++) {
5000 QString keys_str = "keyboard";
5001 bool use_keys = true;
5003 keys_str = "insert";
5008 IntList insertIndex;
5010 QStringList insertString;
5011 QStringList expectedString;
5014 insertMode << NORMAL;
5015 insertString << "1";
5018 insertMode << NORMAL;
5019 insertString << "5";
5022 insertMode << NORMAL;
5023 insertString << "3";
5026 insertMode << NORMAL;
5027 insertString << "2";
5030 insertMode << NORMAL;
5031 insertString << "4";
5033 expectedString << "12345";
5034 expectedString << "1235";
5035 expectedString << "135";
5036 expectedString << "15";
5037 expectedString << "";
5039 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
5047 IntList insertIndex;
5049 QStringList insertString;
5050 QStringList expectedString;
5053 insertMode << NORMAL;
5054 insertString << "World"; // World
5057 insertMode << NORMAL;
5058 insertString << "Hello"; // HelloWorld
5061 insertMode << NORMAL;
5062 insertString << "Well"; // WellHelloWorld
5065 insertMode << NORMAL;
5066 insertString << "There"; // WellHelloThereWorld;
5068 expectedString << "WellHelloThereWorld";
5069 expectedString << "WellHelloWorld";
5070 expectedString << "HelloWorld";
5071 expectedString << "World";
5072 expectedString << "";
5074 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
5082 IntList insertIndex;
5084 QStringList insertString;
5085 QStringList expectedString;
5088 insertMode << NORMAL;
5089 insertString << "Ensuring";
5092 insertMode << NORMAL;
5093 insertString << " instan";
5096 insertMode << NORMAL;
5097 insertString << "an ";
5100 insertMode << REPLACE_UNTIL_END;
5101 insertString << " unique instance.";
5103 expectedString << "Ensuring a unique instance.";
5104 expectedString << "Ensuring an instan";
5105 expectedString << "Ensuring instan";
5106 expectedString << "";
5108 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
5118 void tst_qquicktextinput::undo()
5120 QFETCH(QStringList, insertString);
5121 QFETCH(IntList, insertIndex);
5122 QFETCH(IntList, insertMode);
5123 QFETCH(QStringList, expectedString);
5125 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5126 QQmlComponent textInputComponent(&engine);
5127 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5128 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5129 QVERIFY(textInput != 0);
5131 QQuickWindow window;
5132 textInput->setParentItem(window.contentItem());
5134 window.requestActivateWindow();
5135 QTest::qWaitForWindowActive(&window);
5136 QVERIFY(textInput->hasActiveFocus());
5138 QVERIFY(!textInput->canUndo());
5140 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
5144 // STEP 1: First build up an undo history by inserting or typing some strings...
5145 for (i = 0; i < insertString.size(); ++i) {
5146 if (insertIndex[i] > -1)
5147 textInput->setCursorPosition(insertIndex[i]);
5149 // experimental stuff
5150 if (insertMode[i] == REPLACE_UNTIL_END) {
5151 textInput->select(insertIndex[i], insertIndex[i] + 8);
5153 // This is what I actually want...
5154 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
5157 for (int j = 0; j < insertString.at(i).length(); j++)
5158 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5161 QCOMPARE(spy.count(), 1);
5163 // STEP 2: Next call undo several times and see if we can restore to the previous state
5164 for (i = 0; i < expectedString.size() - 1; ++i) {
5165 QCOMPARE(textInput->text(), expectedString[i]);
5166 QVERIFY(textInput->canUndo());
5170 // STEP 3: Verify that we have undone everything
5171 QVERIFY(textInput->text().isEmpty());
5172 QVERIFY(!textInput->canUndo());
5173 QCOMPARE(spy.count(), 2);
5176 void tst_qquicktextinput::redo_data()
5178 QTest::addColumn<QStringList>("insertString");
5179 QTest::addColumn<IntList>("insertIndex");
5180 QTest::addColumn<QStringList>("expectedString");
5183 IntList insertIndex;
5184 QStringList insertString;
5185 QStringList expectedString;
5188 insertString << "World"; // World
5190 insertString << "Hello"; // HelloWorld
5192 insertString << "Well"; // WellHelloWorld
5194 insertString << "There"; // WellHelloThereWorld;
5196 expectedString << "World";
5197 expectedString << "HelloWorld";
5198 expectedString << "WellHelloWorld";
5199 expectedString << "WellHelloThereWorld";
5201 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
5205 void tst_qquicktextinput::redo()
5207 QFETCH(QStringList, insertString);
5208 QFETCH(IntList, insertIndex);
5209 QFETCH(QStringList, expectedString);
5211 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5212 QQmlComponent textInputComponent(&engine);
5213 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5214 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5215 QVERIFY(textInput != 0);
5217 QQuickWindow window;
5218 textInput->setParentItem(window.contentItem());
5220 window.requestActivateWindow();
5221 QTest::qWaitForWindowActive(&window);
5223 QVERIFY(textInput->hasActiveFocus());
5224 QVERIFY(!textInput->canUndo());
5225 QVERIFY(!textInput->canRedo());
5227 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5230 // inserts the diff strings at diff positions
5231 for (i = 0; i < insertString.size(); ++i) {
5232 if (insertIndex[i] > -1)
5233 textInput->setCursorPosition(insertIndex[i]);
5234 for (int j = 0; j < insertString.at(i).length(); j++)
5235 QTest::keyClick(&window, insertString.at(i).at(j).toLatin1());
5236 QVERIFY(textInput->canUndo());
5237 QVERIFY(!textInput->canRedo());
5240 QCOMPARE(spy.count(), 0);
5243 while (!textInput->text().isEmpty()) {
5244 QVERIFY(textInput->canUndo());
5246 QVERIFY(textInput->canRedo());
5249 QCOMPARE(spy.count(), 1);
5251 for (i = 0; i < expectedString.size(); ++i) {
5252 QVERIFY(textInput->canRedo());
5254 QCOMPARE(textInput->text() , expectedString[i]);
5255 QVERIFY(textInput->canUndo());
5257 QVERIFY(!textInput->canRedo());
5258 QCOMPARE(spy.count(), 2);
5261 void tst_qquicktextinput::undo_keypressevents_data()
5263 QTest::addColumn<KeyList>("keys");
5264 QTest::addColumn<QStringList>("expectedString");
5268 QStringList expectedString;
5271 << QKeySequence::MoveToStartOfLine
5278 << QKeySequence::MoveToEndOfLine
5281 expectedString << "BEVERYAFRAID!";
5282 expectedString << "BEVERYAFRAID";
5283 expectedString << "VERYAFRAID";
5284 expectedString << "AFRAID";
5286 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5289 QStringList expectedString;
5292 keys << "1234" << QKeySequence::MoveToStartOfLine
5294 << Qt::Key_Right << Qt::Key_Right
5296 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5300 expectedString << "12";
5301 expectedString << "1234";
5303 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5306 QStringList expectedString;
5310 << QKeySequence::MoveToStartOfLine
5312 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5313 << Qt::Key_Backspace
5314 << QKeySequence::Undo
5316 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5319 expectedString << "AB";
5320 expectedString << "AB12";
5322 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5325 QStringList expectedString;
5330 << Qt::Key_Left << Qt::Key_Left
5334 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5335 // overwriting '1234' with '5'
5337 // undoing deletion of 'AB'
5338 << QKeySequence::Undo
5339 // overwriting '1234' with '6'
5342 expectedString << "ab6cd";
5343 // for versions previous to 3.2 we overwrite needed two undo operations
5344 expectedString << "ab1234cd";
5345 expectedString << "abcd";
5347 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5350 QStringList expectedString;
5355 << Qt::Key_Backspace;
5357 expectedString << "AB";
5358 expectedString << "ABC";
5360 QTest::newRow("Inserts,backspace") << keys << expectedString;
5363 QStringList expectedString;
5367 << Qt::Key_Backspace
5371 expectedString << "ABZ";
5372 expectedString << "AB";
5373 expectedString << "ABC";
5375 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5378 QStringList expectedString;
5381 keys << "123" << QKeySequence::MoveToStartOfLine
5383 << QKeySequence::SelectEndOfLine
5384 // overwriting '123' with 'ABC'
5387 expectedString << "ABC";
5388 expectedString << "123";
5390 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5393 QStringList expectedString;
5397 << QKeySequence::Undo
5398 << QKeySequence::Redo;
5400 expectedString << "123";
5401 expectedString << QString();
5403 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5406 QStringList expectedString;
5408 keys << "hello world"
5409 << (Qt::Key_Backspace | Qt::ControlModifier)
5410 << QKeySequence::Undo
5411 << QKeySequence::Redo
5420 QTest::newRow("Insert,delete previous word,undo,redo,insert") << keys << expectedString;
5423 QStringList expectedString;
5425 keys << "hello world"
5426 << QKeySequence::SelectPreviousWord
5427 << (Qt::Key_Backspace)
5428 << QKeySequence::Undo
5436 QTest::newRow("Insert,select previous word,remove,undo,insert") << keys << expectedString;
5439 QStringList expectedString;
5441 keys << "hello world"
5442 << QKeySequence::DeleteStartOfWord
5443 << QKeySequence::Undo
5447 << "hello worldhello"
5451 QTest::newRow("Insert,delete previous word,undo,insert") << keys << expectedString;
5454 QStringList expectedString;
5456 keys << "hello world"
5457 << QKeySequence::MoveToPreviousWord
5458 << QKeySequence::DeleteEndOfWord
5459 << QKeySequence::Undo
5463 << "hello helloworld"
5467 QTest::newRow("Insert,move,delete next word,undo,insert") << keys << expectedString;
5469 if (!QKeySequence(QKeySequence::DeleteEndOfLine).isEmpty()) { // X11 only.
5471 QStringList expectedString;
5473 keys << "hello world"
5474 << QKeySequence::MoveToStartOfLine
5476 << QKeySequence::DeleteEndOfLine
5477 << QKeySequence::Undo
5481 << "hhelloello world"
5485 QTest::newRow("Insert,move,delete end of line,undo,insert") << keys << expectedString;
5488 QStringList expectedString;
5490 keys << "hello world"
5491 << QKeySequence::MoveToPreviousWord
5492 << (Qt::Key_Left | Qt::ShiftModifier)
5493 << (Qt::Key_Left | Qt::ShiftModifier)
5494 << QKeySequence::DeleteEndOfWord
5495 << QKeySequence::Undo
5503 QTest::newRow("Insert,move,select,delete next word,undo,insert") << keys << expectedString;
5506 bool canCopyPaste = PlatformQuirks::isClipboardAvailable();
5511 << QKeySequence(QKeySequence::SelectStartOfLine)
5512 << QKeySequence(QKeySequence::Cut)
5514 << QKeySequence(QKeySequence::Paste);
5515 QStringList expectedString = QStringList()
5520 QTest::newRow("Cut,paste") << keys << expectedString;
5525 << QKeySequence(QKeySequence::SelectStartOfLine)
5526 << QKeySequence(QKeySequence::Copy)
5528 << QKeySequence(QKeySequence::Paste);
5529 QStringList expectedString = QStringList()
5534 QTest::newRow("Copy,paste") << keys << expectedString;
5538 void tst_qquicktextinput::undo_keypressevents()
5540 QFETCH(KeyList, keys);
5541 QFETCH(QStringList, expectedString);
5543 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5544 QQmlComponent textInputComponent(&engine);
5545 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5546 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5547 QVERIFY(textInput != 0);
5549 QQuickWindow window;
5550 textInput->setParentItem(window.contentItem());
5552 window.requestActivateWindow();
5553 QTest::qWaitForWindowActive(&window);
5554 QVERIFY(textInput->hasActiveFocus());
5556 simulateKeys(&window, keys);
5558 for (int i = 0; i < expectedString.size(); ++i) {
5559 QCOMPARE(textInput->text() , expectedString[i]);
5562 QVERIFY(textInput->text().isEmpty());
5565 void tst_qquicktextinput::backspaceSurrogatePairs()
5567 // Test backspace, and delete remove both characters in a surrogate pair.
5568 static const quint16 textData[] = { 0xd800, 0xdf00, 0xd800, 0xdf01, 0xd800, 0xdf02, 0xd800, 0xdf03, 0xd800, 0xdf04 };
5569 const QString text = QString::fromUtf16(textData, lengthOf(textData));
5571 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5572 QQmlComponent textInputComponent(&engine);
5573 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5574 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5575 QVERIFY(textInput != 0);
5576 textInput->setText(text);
5577 textInput->setCursorPosition(text.length());
5579 QQuickWindow window;
5580 textInput->setParentItem(window.contentItem());
5582 window.requestActivateWindow();
5583 QVERIFY(QTest::qWaitForWindowActive(&window));
5584 QCOMPARE(QGuiApplication::focusWindow(), &window);
5586 for (int i = text.length(); i >= 0; i -= 2) {
5587 QCOMPARE(textInput->text(), text.mid(0, i));
5588 QTest::keyClick(&window, Qt::Key_Backspace, Qt::NoModifier);
5590 QCOMPARE(textInput->text(), QString());
5592 textInput->setText(text);
5593 textInput->setCursorPosition(0);
5595 for (int i = 0; i < text.length(); i += 2) {
5596 QCOMPARE(textInput->text(), text.mid(i));
5597 QTest::keyClick(&window, Qt::Key_Delete, Qt::NoModifier);
5599 QCOMPARE(textInput->text(), QString());
5602 void tst_qquicktextinput::QTBUG_19956()
5604 QFETCH(QString, url);
5606 QQuickView window(testFileUrl(url));
5608 window.requestActivateWindow();
5609 QTest::qWaitForWindowActive(&window);
5610 QVERIFY(window.rootObject() != 0);
5611 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5613 input->setFocus(true);
5614 QVERIFY(input->hasActiveFocus());
5616 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 30);
5617 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5618 QCOMPARE(window.rootObject()->property("text").toString(), QString("20"));
5619 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5621 window.rootObject()->setProperty("topvalue", 15);
5622 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 15);
5623 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5625 window.rootObject()->setProperty("topvalue", 25);
5626 QCOMPARE(window.rootObject()->property("topvalue").toInt(), 25);
5627 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5629 window.rootObject()->setProperty("bottomvalue", 21);
5630 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 21);
5631 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5633 window.rootObject()->setProperty("bottomvalue", 10);
5634 QCOMPARE(window.rootObject()->property("bottomvalue").toInt(), 10);
5635 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5638 void tst_qquicktextinput::QTBUG_19956_regexp()
5640 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5642 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5643 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5645 QQuickView window(url);
5647 window.requestActivateWindow();
5648 QTest::qWaitForWindowActive(&window);
5649 QVERIFY(window.rootObject() != 0);
5650 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(window.rootObject());
5652 input->setFocus(true);
5653 QVERIFY(input->hasActiveFocus());
5655 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5656 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5657 QCOMPARE(window.rootObject()->property("text").toString(), QString("abc"));
5658 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5660 window.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5661 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5662 QVERIFY(!window.rootObject()->property("acceptableInput").toBool());
5664 window.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5665 QCOMPARE(window.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5666 QVERIFY(window.rootObject()->property("acceptableInput").toBool());
5669 void tst_qquicktextinput::implicitSize_data()
5671 QTest::addColumn<QString>("text");
5672 QTest::addColumn<QString>("wrap");
5673 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5674 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5677 void tst_qquicktextinput::implicitSize()
5679 QFETCH(QString, text);
5680 QFETCH(QString, wrap);
5681 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5682 QQmlComponent textComponent(&engine);
5683 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5684 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5686 QVERIFY(textObject->width() < textObject->implicitWidth());
5687 QVERIFY(textObject->height() == textObject->implicitHeight());
5689 textObject->resetWidth();
5690 QVERIFY(textObject->width() == textObject->implicitWidth());
5691 QVERIFY(textObject->height() == textObject->implicitHeight());
5694 void tst_qquicktextinput::implicitSizeBinding_data()
5696 implicitSize_data();
5699 void tst_qquicktextinput::implicitSizeBinding()
5701 QFETCH(QString, text);
5702 QFETCH(QString, wrap);
5703 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5704 QQmlComponent textComponent(&engine);
5705 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5706 QScopedPointer<QObject> object(textComponent.create());
5707 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5709 QCOMPARE(textObject->width(), textObject->implicitWidth());
5710 QCOMPARE(textObject->height(), textObject->implicitHeight());
5712 textObject->resetWidth();
5713 QCOMPARE(textObject->width(), textObject->implicitWidth());
5714 QCOMPARE(textObject->height(), textObject->implicitHeight());
5716 textObject->resetHeight();
5717 QCOMPARE(textObject->width(), textObject->implicitWidth());
5718 QCOMPARE(textObject->height(), textObject->implicitHeight());
5722 void tst_qquicktextinput::negativeDimensions()
5724 // Verify this doesn't assert during initialization.
5725 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5726 QScopedPointer<QObject> o(component.create());
5728 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5730 QCOMPARE(input->width(), qreal(-1));
5731 QCOMPARE(input->height(), qreal(-1));
5735 void tst_qquicktextinput::setInputMask_data()
5737 QTest::addColumn<QString>("mask");
5738 QTest::addColumn<QString>("input");
5739 QTest::addColumn<QString>("expectedText");
5740 QTest::addColumn<QString>("expectedDisplay");
5741 QTest::addColumn<bool>("insert_text");
5743 // both keyboard and insert()
5744 for (int i=0; i<2; i++) {
5745 bool insert_text = i==0 ? false : true;
5746 QString insert_mode = "keys ";
5748 insert_mode = "insert ";
5750 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5751 << QString("000.000.000.000")
5752 << QString("127.0.0.1")
5753 << QString("127.0.0.1")
5754 << QString("127.0 .0 .1 ")
5755 << bool(insert_text);
5756 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5757 << QString("HH:HH:HH:HH:HH:HH;#")
5758 << QString("00:E0:81:21:9E:8E")
5759 << QString("00:E0:81:21:9E:8E")
5760 << QString("00:E0:81:21:9E:8E")
5761 << bool(insert_text);
5762 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5763 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5764 << QString("AAe081219E8E")
5765 << QString("aa:E0:81:21:9E:8E")
5766 << QString("aa:E0:81:21:9E:8E")
5767 << bool(insert_text);
5768 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5769 << QString("BBBBBBBB;0")
5770 << QString("11011001")
5772 << QString("11011001")
5773 << bool(insert_text);
5774 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5775 << QString("bbbb.bbbb;-")
5776 << QString("110. 0001")
5777 << QString("110.0001")
5778 << QString("110-.0001")
5779 << bool(insert_text);
5780 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5781 << QString("000.000.000.000;0")
5782 << QString("127.0.0.1")
5783 << QString("127...1")
5784 << QString("127.000.000.100")
5785 << bool(insert_text);
5786 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5787 << QString("000.000.000.000")
5788 << QString(".0.0.1")
5789 << QString(".0.0.1")
5790 << QString(" .0 .0 .1 ")
5791 << bool(insert_text);
5792 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5793 << QString("000.000.000.000")
5796 << QString(" . . . ")
5797 << bool(insert_text);
5798 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5799 << QString("000.000.000.000;#")
5802 << QString("###.###.###.###")
5803 << bool(insert_text);
5804 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5805 << QString("000.000.000.000")
5806 << QString("1234123412341234")
5807 << QString("123.412.341.234")
5808 << QString("123.412.341.234")
5809 << bool(insert_text);
5810 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5815 << bool(insert_text);
5816 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5821 << bool(insert_text);
5823 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5828 << bool(insert_text);
5829 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5830 << QString("!A!A!A!A")
5834 << bool(insert_text);
5835 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5840 << bool(insert_text);
5842 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5843 << QString("{n}[0]")
5847 << bool(insert_text);
5848 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5849 << QString("\\\\N\\\\n00")
5853 << bool(insert_text);
5854 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5855 << QString("\\\\\\\\00")
5859 << bool(insert_text);
5860 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5861 << QString("\\\\(00\\\\)")
5865 << bool(insert_text);
5867 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5868 << QString(">AAAA<AAAA!AAAA")
5869 << QString("AbCdEfGhIjKl")
5870 << QString("ABCDefghIjKl")
5871 << QString("ABCDefghIjKl")
5872 << bool(insert_text);
5873 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5874 << QString(">aaaa<aaaa!aaaa")
5875 << QString("AbCdEfGhIjKl")
5876 << QString("ABCDefghIjKl")
5877 << QString("ABCDefghIjKl")
5878 << bool(insert_text);
5880 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5881 << QString(">A<A<A>A>A<A!A!A")
5882 << QString("AbCdEFGH")
5883 << QString("AbcDEfGH")
5884 << QString("AbcDEfGH")
5885 << bool(insert_text);
5886 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5887 << QString(">A<A<A>A>A<A!A!A")
5888 << QString("aBcDefgh")
5889 << QString("AbcDEfgh")
5890 << QString("AbcDEfgh")
5891 << bool(insert_text);
5892 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5893 << QString(">a<a<a>a>a<a!a!a")
5894 << QString("AbCdEFGH")
5895 << QString("AbcDEfGH")
5896 << QString("AbcDEfGH")
5897 << bool(insert_text);
5898 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5899 << QString(">a<a<a>a>a<a!a!a")
5900 << QString("aBcDefgh")
5901 << QString("AbcDEfgh")
5902 << QString("AbcDEfgh")
5903 << bool(insert_text);
5904 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5905 << QString(">H<H<H>H>H<H!H!H")
5906 << QString("aBcDef01")
5907 << QString("AbcDEf01")
5908 << QString("AbcDEf01")
5909 << bool(insert_text);
5910 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5911 << QString(">h<h<h>h>h<h!h!h")
5912 << QString("aBcDef92")
5913 << QString("AbcDEf92")
5914 << QString("AbcDEf92")
5915 << bool(insert_text);
5917 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5918 << QString("AAAAAAAA")
5919 << QString("A2#a;.0!")
5922 << bool(insert_text);
5923 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5925 << QString("f4f4f4f4")
5928 << bool(insert_text);
5929 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5930 << QString("9999;0")
5934 << bool(insert_text);
5938 void tst_qquicktextinput::setInputMask()
5940 QFETCH(QString, mask);
5941 QFETCH(QString, input);
5942 QFETCH(QString, expectedText);
5943 QFETCH(QString, expectedDisplay);
5944 QFETCH(bool, insert_text);
5946 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5947 QQmlComponent textInputComponent(&engine);
5948 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5949 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5950 QVERIFY(textInput != 0);
5952 // then either insert using insert() or keyboard
5954 textInput->insert(0, input);
5956 QQuickWindow window;
5957 textInput->setParentItem(window.contentItem());
5959 window.requestActivateWindow();
5960 QTest::qWaitForWindowActive(&window);
5961 QVERIFY(textInput->hasActiveFocus());
5963 simulateKey(&window, Qt::Key_Home);
5964 for (int i = 0; i < input.length(); i++)
5965 QTest::keyClick(&window, input.at(i).toLatin1());
5968 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5969 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5971 QCOMPARE(textInput->text(), expectedText);
5972 QCOMPARE(textInput->displayText(), expectedDisplay);
5975 void tst_qquicktextinput::inputMask_data()
5977 QTest::addColumn<QString>("mask");
5978 QTest::addColumn<QString>("expectedMask");
5980 // if no mask is set a nul string should be returned
5981 QTest::newRow("nul 1") << QString("") << QString();
5982 QTest::newRow("nul 2") << QString() << QString();
5984 // try different masks
5985 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5986 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5987 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5988 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5990 // set an invalid input mask...
5991 // the current behaviour is that this exact (faulty) string is returned.
5992 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5994 // verify that we can unset the mask again
5995 QTest::newRow("unset") << QString("") << QString();
5998 void tst_qquicktextinput::inputMask()
6000 QFETCH(QString, mask);
6001 QFETCH(QString, expectedMask);
6003 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6004 QQmlComponent textInputComponent(&engine);
6005 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6006 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6007 QVERIFY(textInput != 0);
6009 QCOMPARE(textInput->inputMask(), expectedMask);
6012 void tst_qquicktextinput::clearInputMask()
6014 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
6015 QQmlComponent textInputComponent(&engine);
6016 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6017 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6018 QVERIFY(textInput != 0);
6020 QVERIFY(!textInput->inputMask().isEmpty());
6021 textInput->setInputMask(QString());
6022 QCOMPARE(textInput->inputMask(), QString());
6025 void tst_qquicktextinput::keypress_inputMask_data()
6027 QTest::addColumn<QString>("mask");
6028 QTest::addColumn<KeyList>("keys");
6029 QTest::addColumn<QString>("expectedText");
6030 QTest::addColumn<QString>("expectedDisplayText");
6034 // inserting 'A1.2B'
6035 keys << Qt::Key_Home << "A1.2B";
6036 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
6041 keys << Qt::Key_Home << "0!P3";
6042 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
6047 keys << Qt::Key_Home
6049 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6053 // selecting all and delete
6054 keys << Qt::Key_Home
6055 << Key(Qt::ShiftModifier, Qt::Key_End)
6057 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
6061 // inserting '12.12' then two backspaces
6062 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
6063 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
6068 keys << Qt::Key_Home << "12ab";
6069 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
6073 void tst_qquicktextinput::keypress_inputMask()
6075 QFETCH(QString, mask);
6076 QFETCH(KeyList, keys);
6077 QFETCH(QString, expectedText);
6078 QFETCH(QString, expectedDisplayText);
6080 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
6081 QQmlComponent textInputComponent(&engine);
6082 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6083 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6084 QVERIFY(textInput != 0);
6086 QQuickWindow window;
6087 textInput->setParentItem(window.contentItem());
6089 window.requestActivateWindow();
6090 QTest::qWaitForWindowActive(&window);
6091 QVERIFY(textInput->hasActiveFocus());
6093 simulateKeys(&window, keys);
6095 QCOMPARE(textInput->text(), expectedText);
6096 QCOMPARE(textInput->displayText(), expectedDisplayText);
6100 void tst_qquicktextinput::hasAcceptableInputMask_data()
6102 QTest::addColumn<QString>("optionalMask");
6103 QTest::addColumn<QString>("requiredMask");
6104 QTest::addColumn<QString>("invalid");
6105 QTest::addColumn<QString>("valid");
6107 QTest::newRow("Alphabetic optional and required")
6108 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
6109 QTest::newRow("Alphanumeric optional and require")
6110 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
6111 QTest::newRow("Any optional and required")
6112 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
6113 QTest::newRow("Numeric (0-9) required")
6114 << QString("0000") << QString("9999") << QString("11") << QString("1138");
6115 QTest::newRow("Numeric (1-9) optional and required")
6116 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
6119 void tst_qquicktextinput::hasAcceptableInputMask()
6121 QFETCH(QString, optionalMask);
6122 QFETCH(QString, requiredMask);
6123 QFETCH(QString, invalid);
6124 QFETCH(QString, valid);
6126 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + optionalMask + "\" }";
6127 QQmlComponent textInputComponent(&engine);
6128 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6129 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6130 QVERIFY(textInput != 0);
6132 // test that invalid input (for required) work for optionalMask
6133 textInput->setText(invalid);
6134 QVERIFY(textInput->hasAcceptableInput());
6136 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
6137 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
6139 // test requiredMask
6140 textInput->setInputMask(requiredMask);
6141 textInput->setText(invalid);
6142 QVERIFY(!textInput->hasAcceptableInput());
6144 textInput->setText(valid);
6145 QVERIFY(textInput->hasAcceptableInput());
6148 void tst_qquicktextinput::maskCharacter_data()
6150 QTest::addColumn<QString>("mask");
6151 QTest::addColumn<QString>("input");
6152 QTest::addColumn<bool>("expectedValid");
6154 QTest::newRow("Hex") << QString("H")
6155 << QString("0123456789abcdefABCDEF") << true;
6156 QTest::newRow("hex") << QString("h")
6157 << QString("0123456789abcdefABCDEF") << true;
6158 QTest::newRow("HexInvalid") << QString("H")
6159 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6161 QTest::newRow("hexInvalid") << QString("h")
6162 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
6164 QTest::newRow("Bin") << QString("B")
6165 << QString("01") << true;
6166 QTest::newRow("bin") << QString("b")
6167 << QString("01") << true;
6168 QTest::newRow("BinInvalid") << QString("B")
6169 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6171 QTest::newRow("binInvalid") << QString("b")
6172 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
6176 void tst_qquicktextinput::maskCharacter()
6178 QFETCH(QString, mask);
6179 QFETCH(QString, input);
6180 QFETCH(bool, expectedValid);
6182 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + mask + "\" }";
6183 QQmlComponent textInputComponent(&engine);
6184 textInputComponent.setData(componentStr.toLatin1(), QUrl());
6185 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
6186 QVERIFY(textInput != 0);
6188 for (int i = 0; i < input.size(); ++i) {
6189 QString in = QString(input.at(i));
6190 QString expected = expectedValid ? in : QString();
6191 textInput->setText(QString(input.at(i)));
6192 QCOMPARE(textInput->text(), expected);
6196 QTEST_MAIN(tst_qquicktextinput)
6198 #include "tst_qquicktextinput.moc"