1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/util.h"
44 #include "../../shared/testhttpserver.h"
45 #include <private/qinputmethod_p.h>
46 #include <QtQml/qqmlengine.h>
47 #include <QtQml/qqmlexpression.h>
49 #include <QtQuick/qquickview.h>
50 #include <QtGui/qguiapplication.h>
51 #include <QtGui/qstylehints.h>
52 #include <QInputMethod>
53 #include <private/qquicktextinput_p.h>
54 #include <private/qquicktextinput_p_p.h>
60 #include <Carbon/Carbon.h>
63 #include "qplatformdefs.h"
64 #include "../../shared/platforminputcontext.h"
66 #define SERVER_PORT 14460
67 #define SERVER_ADDR "http://localhost:14460"
69 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
70 Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
71 Q_DECLARE_METATYPE(Qt::Key)
73 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
75 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
77 // XXX This will be replaced by some clever persistent platform image store.
78 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
79 QString arch = "unknown-architecture"; // QTest needs to help with this.
81 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
83 if (!QFile::exists(expectfile)) {
84 actual.save(expectfile);
85 qWarning() << "created" << expectfile;
91 template <typename T> static T evaluate(QObject *scope, const QString &expression)
93 QQmlExpression expr(qmlContext(scope), scope, expression);
94 T result = expr.evaluate().value<T>();
96 qWarning() << expr.error().toString();
100 template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
102 typedef QPair<int, QChar> Key;
104 class tst_qquicktextinput : public QQmlDataTest
109 tst_qquicktextinput();
119 void persistentSelection();
120 void isRightToLeft_data();
121 void isRightToLeft();
122 void moveCursorSelection_data();
123 void moveCursorSelection();
124 void moveCursorSelectionSequence_data();
125 void moveCursorSelectionSequence();
126 void dragMouseSelection();
127 void mouseSelectionMode_data();
128 void mouseSelectionMode();
129 void tripleClickSelectsAll();
131 void horizontalAlignment_data();
132 void horizontalAlignment();
133 void horizontalAlignment_RightToLeft();
134 void verticalAlignment();
146 void passwordCharacter();
147 void cursorDelegate_data();
148 void cursorDelegate();
149 void remoteCursorDelegate();
150 void cursorVisible();
151 void cursorRectangle_data();
152 void cursorRectangle();
154 void navigation_RTL();
156 void copyAndPasteKeySequence();
157 void canPasteEmpty();
162 void openInputPanel();
163 void setHAlignClearCache();
164 void focusOutClearSelection();
167 void passwordEchoDelay();
168 void geometrySignals();
171 void preeditAutoScroll();
172 void preeditCursorRectangle();
173 void inputContextMouseHandler();
174 void inputMethodComposing();
175 void inputMethodUpdate();
176 void cursorRectangleSize();
185 void keySequence_data();
192 void undo_keypressevents_data();
193 void undo_keypressevents();
196 void QTBUG_19956_data();
197 void QTBUG_19956_regexp();
199 void implicitSize_data();
201 void implicitSizeBinding_data();
202 void implicitSizeBinding();
204 void negativeDimensions();
207 void setInputMask_data();
209 void inputMask_data();
211 void clearInputMask();
212 void keypress_inputMask_data();
213 void keypress_inputMask();
214 void hasAcceptableInputMask_data();
215 void hasAcceptableInputMask();
216 void maskCharacter_data();
217 void maskCharacter();
220 void simulateKey(QWindow *, int key);
222 void simulateKeys(QWindow *window, const QList<Key> &keys);
223 void simulateKeys(QWindow *window, const QKeySequence &sequence);
226 QStringList standard;
227 QStringList colorStrings;
230 typedef QList<int> IntList;
231 Q_DECLARE_METATYPE(IntList)
233 typedef QList<Key> KeyList;
234 Q_DECLARE_METATYPE(KeyList)
236 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
238 for (int i = 0; i < keys.count(); ++i) {
239 const int key = keys.at(i).first;
240 const int modifiers = key & Qt::KeyboardModifierMask;
241 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
243 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
244 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
246 QGuiApplication::sendEvent(window, &press);
247 QGuiApplication::sendEvent(window, &release);
251 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
253 for (int i = 0; i < sequence.count(); ++i) {
254 const int key = sequence[i];
255 const int modifiers = key & Qt::KeyboardModifierMask;
257 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
261 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
263 for (int i = 0; i < sequence.count(); ++i)
264 keys << Key(sequence[i], QChar());
268 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
270 for (int i = 0; i < N - 1; ++i) {
271 int key = QTest::asciiToKey(characters[i]);
272 QChar character = QLatin1Char(characters[i]);
273 keys << Key(key, character);
278 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
280 keys << Key(key, QChar());
284 void tst_qquicktextinput::cleanup()
286 // ensure not even skipped tests with custom input context leave it dangling
287 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
288 inputMethodPrivate->testContext = 0;
291 tst_qquicktextinput::tst_qquicktextinput()
293 standard << "the quick brown fox jumped over the lazy dog"
294 << "It's supercalifragisiticexpialidocious!"
299 colorStrings << "aliceblue"
313 void tst_qquicktextinput::text()
316 QQmlComponent textinputComponent(&engine);
317 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
318 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
320 QVERIFY(textinputObject != 0);
321 QCOMPARE(textinputObject->text(), QString(""));
322 QCOMPARE(textinputObject->length(), 0);
324 delete textinputObject;
327 for (int i = 0; i < standard.size(); i++)
329 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
330 QQmlComponent textinputComponent(&engine);
331 textinputComponent.setData(componentStr.toLatin1(), QUrl());
332 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
334 QVERIFY(textinputObject != 0);
335 QCOMPARE(textinputObject->text(), standard.at(i));
336 QCOMPARE(textinputObject->length(), standard.at(i).length());
338 delete textinputObject;
343 void tst_qquicktextinput::width()
345 // uses Font metrics to find the width for standard
347 QQmlComponent textinputComponent(&engine);
348 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
349 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
351 QVERIFY(textinputObject != 0);
352 QCOMPARE(textinputObject->width(), 0.0);
354 delete textinputObject;
357 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
359 for (int i = 0; i < standard.size(); i++)
361 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
362 QQmlComponent textinputComponent(&engine);
363 textinputComponent.setData(componentStr.toLatin1(), QUrl());
364 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
366 QString s = standard.at(i);
367 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
369 QTextLayout layout(s);
370 layout.setFont(textinputObject->font());
371 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
372 if (requiresUnhintedMetrics) {
374 option.setUseDesignMetrics(true);
375 layout.setTextOption(option);
378 layout.beginLayout();
380 QTextLine line = layout.createLine();
387 qreal metricWidth = ceil(layout.boundingRect().width());
389 QVERIFY(textinputObject != 0);
390 int delta = abs(int(int(textinputObject->width()) - metricWidth));
391 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
393 delete textinputObject;
397 void tst_qquicktextinput::font()
399 //test size, then bold, then italic, then family
401 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
402 QQmlComponent textinputComponent(&engine);
403 textinputComponent.setData(componentStr.toLatin1(), QUrl());
404 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
406 QVERIFY(textinputObject != 0);
407 QCOMPARE(textinputObject->font().pointSize(), 40);
408 QCOMPARE(textinputObject->font().bold(), false);
409 QCOMPARE(textinputObject->font().italic(), false);
411 delete textinputObject;
415 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
416 QQmlComponent textinputComponent(&engine);
417 textinputComponent.setData(componentStr.toLatin1(), QUrl());
418 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
420 QVERIFY(textinputObject != 0);
421 QCOMPARE(textinputObject->font().bold(), true);
422 QCOMPARE(textinputObject->font().italic(), false);
424 delete textinputObject;
428 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
429 QQmlComponent textinputComponent(&engine);
430 textinputComponent.setData(componentStr.toLatin1(), QUrl());
431 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
433 QVERIFY(textinputObject != 0);
434 QCOMPARE(textinputObject->font().italic(), true);
435 QCOMPARE(textinputObject->font().bold(), false);
437 delete textinputObject;
441 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
442 QQmlComponent textinputComponent(&engine);
443 textinputComponent.setData(componentStr.toLatin1(), QUrl());
444 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
446 QVERIFY(textinputObject != 0);
447 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
448 QCOMPARE(textinputObject->font().bold(), false);
449 QCOMPARE(textinputObject->font().italic(), false);
451 delete textinputObject;
455 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
456 QQmlComponent textinputComponent(&engine);
457 textinputComponent.setData(componentStr.toLatin1(), QUrl());
458 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
460 QVERIFY(textinputObject != 0);
461 QCOMPARE(textinputObject->font().family(), QString(""));
463 delete textinputObject;
467 void tst_qquicktextinput::color()
470 for (int i = 0; i < colorStrings.size(); i++)
472 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
473 QQmlComponent textinputComponent(&engine);
474 textinputComponent.setData(componentStr.toLatin1(), QUrl());
475 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
476 QVERIFY(textinputObject != 0);
477 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
479 delete textinputObject;
482 //test selection color
483 for (int i = 0; i < colorStrings.size(); i++)
485 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
486 QQmlComponent textinputComponent(&engine);
487 textinputComponent.setData(componentStr.toLatin1(), QUrl());
488 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
489 QVERIFY(textinputObject != 0);
490 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
492 delete textinputObject;
495 //test selected text color
496 for (int i = 0; i < colorStrings.size(); i++)
498 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
499 QQmlComponent textinputComponent(&engine);
500 textinputComponent.setData(componentStr.toLatin1(), QUrl());
501 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
502 QVERIFY(textinputObject != 0);
503 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
505 delete textinputObject;
509 QString colorStr = "#AA001234";
510 QColor testColor("#001234");
511 testColor.setAlpha(170);
513 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
514 QQmlComponent textinputComponent(&engine);
515 textinputComponent.setData(componentStr.toLatin1(), QUrl());
516 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
518 QVERIFY(textinputObject != 0);
519 QCOMPARE(textinputObject->color(), testColor);
521 delete textinputObject;
525 void tst_qquicktextinput::wrap()
528 // for specified width and wrap set true
530 QQmlComponent textComponent(&engine);
531 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
532 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
533 textHeight = textObject->height();
535 QVERIFY(textObject != 0);
536 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
537 QCOMPARE(textObject->width(), 300.);
542 for (int i = 0; i < standard.count(); i++) {
543 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
544 QQmlComponent textComponent(&engine);
545 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
546 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
548 QVERIFY(textObject != 0);
549 QCOMPARE(textObject->width(), 30.);
550 QVERIFY(textObject->height() > textHeight);
552 int oldHeight = textObject->height();
553 textObject->setWidth(100);
554 QVERIFY(textObject->height() < oldHeight);
560 void tst_qquicktextinput::selection()
562 QString testStr = standard[0];
563 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
564 QQmlComponent textinputComponent(&engine);
565 textinputComponent.setData(componentStr.toLatin1(), QUrl());
566 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
567 QVERIFY(textinputObject != 0);
570 //Test selection follows cursor
571 for (int i=0; i<= testStr.size(); i++) {
572 textinputObject->setCursorPosition(i);
573 QCOMPARE(textinputObject->cursorPosition(), i);
574 QCOMPARE(textinputObject->selectionStart(), i);
575 QCOMPARE(textinputObject->selectionEnd(), i);
576 QVERIFY(textinputObject->selectedText().isNull());
579 textinputObject->setCursorPosition(0);
580 QVERIFY(textinputObject->cursorPosition() == 0);
581 QVERIFY(textinputObject->selectionStart() == 0);
582 QVERIFY(textinputObject->selectionEnd() == 0);
583 QVERIFY(textinputObject->selectedText().isNull());
585 // Verify invalid positions are ignored.
586 textinputObject->setCursorPosition(-1);
587 QVERIFY(textinputObject->cursorPosition() == 0);
588 QVERIFY(textinputObject->selectionStart() == 0);
589 QVERIFY(textinputObject->selectionEnd() == 0);
590 QVERIFY(textinputObject->selectedText().isNull());
592 textinputObject->setCursorPosition(textinputObject->text().count()+1);
593 QVERIFY(textinputObject->cursorPosition() == 0);
594 QVERIFY(textinputObject->selectionStart() == 0);
595 QVERIFY(textinputObject->selectionEnd() == 0);
596 QVERIFY(textinputObject->selectedText().isNull());
599 for (int i=0; i<= testStr.size(); i++) {
600 textinputObject->select(0,i);
601 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
603 for (int i=0; i<= testStr.size(); i++) {
604 textinputObject->select(i,testStr.size());
605 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
608 textinputObject->setCursorPosition(0);
609 QVERIFY(textinputObject->cursorPosition() == 0);
610 QVERIFY(textinputObject->selectionStart() == 0);
611 QVERIFY(textinputObject->selectionEnd() == 0);
612 QVERIFY(textinputObject->selectedText().isNull());
614 //Test Error Ignoring behaviour
615 textinputObject->setCursorPosition(0);
616 QVERIFY(textinputObject->selectedText().isNull());
617 textinputObject->select(-10,0);
618 QVERIFY(textinputObject->selectedText().isNull());
619 textinputObject->select(100,110);
620 QVERIFY(textinputObject->selectedText().isNull());
621 textinputObject->select(0,-10);
622 QVERIFY(textinputObject->selectedText().isNull());
623 textinputObject->select(0,100);
624 QVERIFY(textinputObject->selectedText().isNull());
625 textinputObject->select(0,10);
626 QVERIFY(textinputObject->selectedText().size() == 10);
627 textinputObject->select(-10,10);
628 QVERIFY(textinputObject->selectedText().size() == 10);
629 textinputObject->select(100,101);
630 QVERIFY(textinputObject->selectedText().size() == 10);
631 textinputObject->select(0,-10);
632 QVERIFY(textinputObject->selectedText().size() == 10);
633 textinputObject->select(0,100);
634 QVERIFY(textinputObject->selectedText().size() == 10);
636 textinputObject->deselect();
637 QVERIFY(textinputObject->selectedText().isNull());
638 textinputObject->select(0,10);
639 QVERIFY(textinputObject->selectedText().size() == 10);
640 textinputObject->deselect();
641 QVERIFY(textinputObject->selectedText().isNull());
643 // test input method selection
644 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
645 textinputObject->setFocus(true);
647 QList<QInputMethodEvent::Attribute> attributes;
648 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
649 QInputMethodEvent event("", attributes);
650 QGuiApplication::sendEvent(textinputObject, &event);
652 QCOMPARE(selectionSpy.count(), 1);
653 QCOMPARE(textinputObject->selectionStart(), 12);
654 QCOMPARE(textinputObject->selectionEnd(), 17);
656 delete textinputObject;
659 void tst_qquicktextinput::persistentSelection()
661 QQuickView canvas(testFileUrl("persistentSelection.qml"));
663 canvas.requestActivateWindow();
664 QTest::qWaitForWindowActive(&canvas);
666 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
668 QVERIFY(input->hasActiveFocus());
670 QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
672 QCOMPARE(input->persistentSelection(), false);
674 input->setPersistentSelection(false);
675 QCOMPARE(input->persistentSelection(), false);
676 QCOMPARE(spy.count(), 0);
679 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
681 input->setFocus(false);
682 QCOMPARE(input->property("selected").toString(), QString());
684 input->setFocus(true);
685 QCOMPARE(input->property("selected").toString(), QString());
687 input->setPersistentSelection(true);
688 QCOMPARE(input->persistentSelection(), true);
689 QCOMPARE(spy.count(), 1);
692 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
694 input->setFocus(false);
695 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
697 input->setFocus(true);
698 QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
701 void tst_qquicktextinput::isRightToLeft_data()
703 QTest::addColumn<QString>("text");
704 QTest::addColumn<bool>("emptyString");
705 QTest::addColumn<bool>("firstCharacter");
706 QTest::addColumn<bool>("lastCharacter");
707 QTest::addColumn<bool>("middleCharacter");
708 QTest::addColumn<bool>("startString");
709 QTest::addColumn<bool>("midString");
710 QTest::addColumn<bool>("endString");
712 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
713 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
714 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
715 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
716 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
717 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;
718 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
721 void tst_qquicktextinput::isRightToLeft()
723 QFETCH(QString, text);
724 QFETCH(bool, emptyString);
725 QFETCH(bool, firstCharacter);
726 QFETCH(bool, lastCharacter);
727 QFETCH(bool, middleCharacter);
728 QFETCH(bool, startString);
729 QFETCH(bool, midString);
730 QFETCH(bool, endString);
732 QQuickTextInput textInput;
733 textInput.setText(text);
735 // first test that the right string is delivered to the QString::isRightToLeft()
736 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
737 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
738 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
739 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
740 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
741 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
743 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
744 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
746 // then test that the feature actually works
747 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
748 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
749 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
750 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
751 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
752 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
754 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
755 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
758 void tst_qquicktextinput::moveCursorSelection_data()
760 QTest::addColumn<QString>("testStr");
761 QTest::addColumn<int>("cursorPosition");
762 QTest::addColumn<int>("movePosition");
763 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
764 QTest::addColumn<int>("selectionStart");
765 QTest::addColumn<int>("selectionEnd");
766 QTest::addColumn<bool>("reversible");
768 // () contains the text selected by the cursor.
769 // <> contains the actual selection.
771 QTest::newRow("(t)he|characters")
772 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
773 QTest::newRow("do(g)|characters")
774 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
775 QTest::newRow("jum(p)ed|characters")
776 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
777 QTest::newRow("jumped( )over|characters")
778 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
779 QTest::newRow("(the )|characters")
780 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
781 QTest::newRow("( dog)|characters")
782 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
783 QTest::newRow("( jumped )|characters")
784 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
785 QTest::newRow("th(e qu)ick|characters")
786 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
787 QTest::newRow("la(zy d)og|characters")
788 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
789 QTest::newRow("jum(ped ov)er|characters")
790 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
791 QTest::newRow("()the|characters")
792 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
793 QTest::newRow("dog()|characters")
794 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
795 QTest::newRow("jum()ped|characters")
796 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
798 QTest::newRow("<(t)he>|words")
799 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
800 QTest::newRow("<do(g)>|words")
801 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
802 QTest::newRow("<jum(p)ed>|words")
803 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
804 QTest::newRow("<jumped( )>over|words,ltr")
805 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
806 QTest::newRow("jumped<( )over>|words,rtl")
807 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
808 QTest::newRow("<(the )>quick|words,ltr")
809 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
810 QTest::newRow("<(the )quick>|words,rtl")
811 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
812 QTest::newRow("<lazy( dog)>|words,ltr")
813 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
814 QTest::newRow("lazy<( dog)>|words,rtl")
815 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
816 QTest::newRow("<fox( jumped )>over|words,ltr")
817 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
818 QTest::newRow("fox<( jumped )over>|words,rtl")
819 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
820 QTest::newRow("<th(e qu)ick>|words")
821 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
822 QTest::newRow("<la(zy d)og|words>")
823 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
824 QTest::newRow("<jum(ped ov)er>|words")
825 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
826 QTest::newRow("<()>the|words")
827 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
828 QTest::newRow("dog<()>|words")
829 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
830 QTest::newRow("jum<()>ped|words")
831 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
833 QTest::newRow("Hello<(,)> |words")
834 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
835 QTest::newRow("Hello<(, )>world|words,ltr")
836 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
837 QTest::newRow("Hello<(, )world>|words,rtl")
838 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
839 QTest::newRow("<Hel(lo, )>world|words,ltr")
840 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
841 QTest::newRow("<Hel(lo, )world>|words,rtl")
842 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
843 QTest::newRow("<Hel(lo)>,|words")
844 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
845 QTest::newRow("Hello<()>,|words")
846 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
847 QTest::newRow("Hello,<()>|words")
848 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
849 QTest::newRow("Hello<,( )>world|words,ltr")
850 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
851 QTest::newRow("Hello,<( )world>|words,rtl")
852 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
853 QTest::newRow("Hello<,( world)>|words,ltr")
854 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
855 QTest::newRow("Hello,<( world)>|words,rtl")
856 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
857 QTest::newRow("Hello<,( world!)>|words,ltr")
858 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
859 QTest::newRow("Hello,<( world!)>|words,rtl")
860 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
861 QTest::newRow("Hello<(, world!)>|words")
862 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
863 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
865 // QTest::newRow("world<(!)>|words")
866 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
867 QTest::newRow("world!<()>)|words")
868 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
869 QTest::newRow("world<()>!)|words")
870 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
872 QTest::newRow("<(,)>olleH |words")
873 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
874 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
875 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
876 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
877 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
878 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
879 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
880 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
881 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
882 QTest::newRow(",<(ol)leH>,|words")
883 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
884 QTest::newRow(",<()>olleH|words")
885 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
886 QTest::newRow("<()>,olleH|words")
887 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
888 QTest::newRow("<dlrow( )>,olleH|words,ltr")
889 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
890 QTest::newRow("dlrow<( ),>olleH|words,rtl")
891 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
892 QTest::newRow("<(dlrow )>,olleH|words,ltr")
893 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
894 QTest::newRow("<(dlrow ),>olleH|words,rtl")
895 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
896 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
897 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
898 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
899 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
900 QTest::newRow("(!dlrow ,)olleH|words")
901 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
902 QTest::newRow("<(!)>dlrow|words")
903 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
904 QTest::newRow("<()>!dlrow|words")
905 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
906 QTest::newRow("!<()>dlrow|words")
907 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
909 QTest::newRow(" <s(pac)ey> text |words")
910 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
911 QTest::newRow(" spacey <t(ex)t> |words")
912 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
913 QTest::newRow("<( )>spacey text |words|ltr")
914 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
915 QTest::newRow("<( )spacey> text |words|rtl")
916 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
917 QTest::newRow("spacey <text( )>|words|ltr")
918 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
920 // QTest::newRow("spacey text<( )>|words|rtl")
921 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
922 QTest::newRow("<()> spacey text |words")
923 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
924 QTest::newRow(" spacey text <()>|words")
925 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
928 void tst_qquicktextinput::moveCursorSelection()
930 QFETCH(QString, testStr);
931 QFETCH(int, cursorPosition);
932 QFETCH(int, movePosition);
933 QFETCH(QQuickTextInput::SelectionMode, mode);
934 QFETCH(int, selectionStart);
935 QFETCH(int, selectionEnd);
936 QFETCH(bool, reversible);
938 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
939 QQmlComponent textinputComponent(&engine);
940 textinputComponent.setData(componentStr.toLatin1(), QUrl());
941 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
942 QVERIFY(textinputObject != 0);
944 textinputObject->setCursorPosition(cursorPosition);
945 textinputObject->moveCursorSelection(movePosition, mode);
947 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
948 QCOMPARE(textinputObject->selectionStart(), selectionStart);
949 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
952 textinputObject->setCursorPosition(movePosition);
953 textinputObject->moveCursorSelection(cursorPosition, mode);
955 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
956 QCOMPARE(textinputObject->selectionStart(), selectionStart);
957 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
960 delete textinputObject;
963 void tst_qquicktextinput::moveCursorSelectionSequence_data()
965 QTest::addColumn<QString>("testStr");
966 QTest::addColumn<int>("cursorPosition");
967 QTest::addColumn<int>("movePosition1");
968 QTest::addColumn<int>("movePosition2");
969 QTest::addColumn<int>("selection1Start");
970 QTest::addColumn<int>("selection1End");
971 QTest::addColumn<int>("selection2Start");
972 QTest::addColumn<int>("selection2End");
974 // () contains the text selected by the cursor.
975 // <> contains the actual selection.
976 // ^ is the revised cursor position.
977 // {} contains the revised selection.
979 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
984 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
989 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
994 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
999 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1004 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1009 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1014 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1019 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1024 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1029 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1034 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1039 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1044 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1049 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1054 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1059 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1064 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1070 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1075 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1081 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1086 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1092 QTest::newRow("{<(^} sp)acey> text |ltr")
1097 QTest::newRow("{<( ^}sp)acey> text |ltr")
1102 QTest::newRow("<( {s^p)acey>} text |rtl")
1107 QTest::newRow("<( {^sp)acey>} text |rtl")
1113 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1118 QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1123 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1128 QTest::newRow(" spacey {<te(xt^} )>|ltr")
1135 void tst_qquicktextinput::moveCursorSelectionSequence()
1137 QFETCH(QString, testStr);
1138 QFETCH(int, cursorPosition);
1139 QFETCH(int, movePosition1);
1140 QFETCH(int, movePosition2);
1141 QFETCH(int, selection1Start);
1142 QFETCH(int, selection1End);
1143 QFETCH(int, selection2Start);
1144 QFETCH(int, selection2End);
1146 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1147 QQmlComponent textinputComponent(&engine);
1148 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1149 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1150 QVERIFY(textinputObject != 0);
1152 textinputObject->setCursorPosition(cursorPosition);
1154 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1155 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1156 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1157 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1159 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1160 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1161 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1162 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1164 delete textinputObject;
1167 void tst_qquicktextinput::dragMouseSelection()
1169 QString qmlfile = testFile("mouseselection_true.qml");
1171 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1174 canvas.requestActivateWindow();
1175 QTest::qWaitForWindowActive(&canvas);
1177 QVERIFY(canvas.rootObject() != 0);
1178 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1179 QVERIFY(textInputObject != 0);
1181 // press-and-drag-and-release from x1 to x2
1184 int y = textInputObject->height()/2;
1185 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1186 QTest::mouseMove(&canvas, QPoint(x2, y));
1187 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1190 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1191 QVERIFY(str1.length() > 3);
1193 // press and drag the current selection.
1196 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1197 QTest::mouseMove(&canvas, QPoint(x2, y));
1198 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1200 QString str2 = textInputObject->selectedText();
1201 QVERIFY(str2.length() > 3);
1203 QVERIFY(str1 != str2);
1206 void tst_qquicktextinput::mouseSelectionMode_data()
1208 QTest::addColumn<QString>("qmlfile");
1209 QTest::addColumn<bool>("selectWords");
1210 QTest::addColumn<bool>("focus");
1211 QTest::addColumn<bool>("focusOnPress");
1214 QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1215 QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1216 QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1217 QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1218 QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1219 QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1220 QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1221 QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1222 QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1225 void tst_qquicktextinput::mouseSelectionMode()
1227 QFETCH(QString, qmlfile);
1228 QFETCH(bool, selectWords);
1229 QFETCH(bool, focus);
1230 QFETCH(bool, focusOnPress);
1232 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1234 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1237 canvas.requestActivateWindow();
1238 QTest::qWaitForWindowActive(&canvas);
1240 QVERIFY(canvas.rootObject() != 0);
1241 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1242 QVERIFY(textInputObject != 0);
1244 textInputObject->setFocus(focus);
1245 textInputObject->setFocusOnPress(focusOnPress);
1247 // press-and-drag-and-release from x1 to x2
1250 int y = textInputObject->height()/2;
1251 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1252 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1253 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1256 QTRY_COMPARE(textInputObject->selectedText(), text);
1258 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1259 QVERIFY(textInputObject->selectedText() != text);
1263 void tst_qquicktextinput::horizontalAlignment_data()
1265 QTest::addColumn<int>("hAlign");
1266 QTest::addColumn<QString>("expectfile");
1268 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1269 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1270 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1273 void tst_qquicktextinput::horizontalAlignment()
1275 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1277 QFETCH(int, hAlign);
1278 QFETCH(QString, expectfile);
1280 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1283 QTest::qWaitForWindowShown(&canvas);
1285 QObject *ob = canvas.rootObject();
1287 ob->setProperty("horizontalAlignment",hAlign);
1288 QImage actual = canvas.grabFrameBuffer();
1290 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1292 QImage expect(expectfile);
1294 QCOMPARE(actual,expect);
1297 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1299 PlatformInputContext platformInputContext;
1300 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1301 inputMethodPrivate->testContext = &platformInputContext;
1303 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1304 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1305 QVERIFY(textInput != 0);
1308 const QString rtlText = textInput->text();
1310 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1311 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1313 // implicit alignment should follow the reading direction of RTL text
1314 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1315 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1316 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1317 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1319 // explicitly left aligned
1320 textInput->setHAlign(QQuickTextInput::AlignLeft);
1321 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1322 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1323 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1325 // explicitly right aligned
1326 textInput->setHAlign(QQuickTextInput::AlignRight);
1327 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1328 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1329 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1330 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1332 // explicitly center aligned
1333 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1334 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1335 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1336 QVERIFY(textInput->boundingRect().left() > 0);
1337 QVERIFY(textInput->boundingRect().right() < textInput->width());
1339 // reseted alignment should go back to following the text reading direction
1340 textInput->resetHAlign();
1341 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1342 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1343 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1344 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1346 // mirror the text item
1347 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1349 // mirrored implicit alignment should continue to follow the reading direction of the text
1350 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1351 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1352 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1353 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1355 // explicitly right aligned behaves as left aligned
1356 textInput->setHAlign(QQuickTextInput::AlignRight);
1357 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1358 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1359 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1361 // mirrored explicitly left aligned behaves as right aligned
1362 textInput->setHAlign(QQuickTextInput::AlignLeft);
1363 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1364 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1365 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1366 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1368 // disable mirroring
1369 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1370 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1371 textInput->resetHAlign();
1373 // English text should be implicitly left aligned
1374 textInput->setText("Hello world!");
1375 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1376 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1378 canvas.requestActivateWindow();
1379 QTest::qWaitForWindowActive(&canvas);
1380 QVERIFY(textInput->hasActiveFocus());
1382 // If there is no commited text, the preedit text should determine the alignment.
1383 textInput->setText(QString());
1384 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1385 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1386 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textInput, &ev); }
1387 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1389 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1390 // redundant as an actual input method may take care of it.
1391 { QInputMethodEvent ev; QGuiApplication::sendEvent(textInput, &ev); }
1393 // empty text with implicit alignment follows the system locale-based
1394 // keyboard input direction from QInputMethod::inputDirection()
1395 textInput->setText("");
1396 platformInputContext.setInputDirection(Qt::LeftToRight);
1397 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1398 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1399 QCOMPARE(textInput->boundingRect().left(), qreal(0));
1401 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1402 platformInputContext.setInputDirection(Qt::RightToLeft);
1403 QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1404 QCOMPARE(cursorRectangleSpy.count(), 1);
1405 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1406 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1407 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1409 // set input direction while having content
1410 platformInputContext.setInputDirection(Qt::LeftToRight);
1411 textInput->setText("a");
1412 platformInputContext.setInputDirection(Qt::RightToLeft);
1413 QTest::keyClick(&canvas, Qt::Key_Backspace);
1414 QVERIFY(textInput->text().isEmpty());
1415 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1416 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1417 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1419 // input direction changed while not having focus
1420 platformInputContext.setInputDirection(Qt::LeftToRight);
1421 textInput->setFocus(false);
1422 platformInputContext.setInputDirection(Qt::RightToLeft);
1423 textInput->setFocus(true);
1424 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1425 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1426 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1428 textInput->setHAlign(QQuickTextInput::AlignRight);
1429 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1430 QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1431 QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1433 // neutral text should fall back to input direction
1434 textInput->setFocus(true);
1435 textInput->resetHAlign();
1436 textInput->setText(" ()((=<>");
1437 platformInputContext.setInputDirection(Qt::LeftToRight);
1438 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1439 platformInputContext.setInputDirection(Qt::RightToLeft);
1440 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1442 // changing width keeps right aligned cursor on proper position
1443 textInput->setText("");
1444 textInput->setWidth(500);
1445 QVERIFY(textInput->positionToRectangle(0).x() > textInput->width() / 2);
1448 void tst_qquicktextinput::verticalAlignment()
1450 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1451 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1452 QVERIFY(textInput != 0);
1455 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1456 QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1457 QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
1458 QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
1461 textInput->setVAlign(QQuickTextInput::AlignBottom);
1462 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1463 QVERIFY(textInput->boundingRect().top() > canvas.height() / 2);
1464 QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
1465 QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
1467 // explicitly center aligned
1468 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1469 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1470 QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1471 QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1472 QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
1473 QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
1474 QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
1475 QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
1478 void tst_qquicktextinput::clipRect()
1480 QQmlComponent component(&engine);
1481 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1482 QScopedPointer<QObject> object(component.create());
1483 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1486 QCOMPARE(input->clipRect().x(), qreal(0));
1487 QCOMPARE(input->clipRect().y(), qreal(0));
1488 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1489 QCOMPARE(input->clipRect().height(), input->height());
1491 input->setText("Hello World");
1492 QCOMPARE(input->clipRect().x(), qreal(0));
1493 QCOMPARE(input->clipRect().y(), qreal(0));
1494 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1495 QCOMPARE(input->clipRect().height(), input->height());
1497 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1498 input->setWidth(input->width() / 2);
1499 QCOMPARE(input->clipRect().x(), qreal(0));
1500 QCOMPARE(input->clipRect().y(), qreal(0));
1501 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1502 QCOMPARE(input->clipRect().height(), input->height());
1504 input->setHeight(input->height() * 2);
1505 QCOMPARE(input->clipRect().x(), qreal(0));
1506 QCOMPARE(input->clipRect().y(), qreal(0));
1507 QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1508 QCOMPARE(input->clipRect().height(), input->height());
1510 QQmlComponent cursorComponent(&engine);
1511 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1513 input->setCursorDelegate(&cursorComponent);
1514 input->setCursorVisible(true);
1516 // If a cursor delegate is used it's size should determine the excess width.
1517 QCOMPARE(input->clipRect().x(), qreal(0));
1518 QCOMPARE(input->clipRect().y(), qreal(0));
1519 QCOMPARE(input->clipRect().width(), input->width() + 8);
1520 QCOMPARE(input->clipRect().height(), input->height());
1522 // Alignment, auto scroll, wrapping all don't affect the clip rect.
1523 input->setAutoScroll(false);
1524 QCOMPARE(input->clipRect().x(), qreal(0));
1525 QCOMPARE(input->clipRect().y(), qreal(0));
1526 QCOMPARE(input->clipRect().width(), input->width() + 8);
1527 QCOMPARE(input->clipRect().height(), input->height());
1529 input->setHAlign(QQuickTextInput::AlignRight);
1530 QCOMPARE(input->clipRect().x(), qreal(0));
1531 QCOMPARE(input->clipRect().y(), qreal(0));
1532 QCOMPARE(input->clipRect().width(), input->width() + 8);
1533 QCOMPARE(input->clipRect().height(), input->height());
1535 input->setWrapMode(QQuickTextInput::Wrap);
1536 QCOMPARE(input->clipRect().x(), qreal(0));
1537 QCOMPARE(input->clipRect().y(), qreal(0));
1538 QCOMPARE(input->clipRect().width(), input->width() + 8);
1539 QCOMPARE(input->clipRect().height(), input->height());
1541 input->setVAlign(QQuickTextInput::AlignBottom);
1542 QCOMPARE(input->clipRect().x(), qreal(0));
1543 QCOMPARE(input->clipRect().y(), qreal(0));
1544 QCOMPARE(input->clipRect().width(), input->width() + 8);
1545 QCOMPARE(input->clipRect().height(), input->height());
1548 void tst_qquicktextinput::boundingRect()
1550 QQmlComponent component(&engine);
1551 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1552 QScopedPointer<QObject> object(component.create());
1553 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1557 layout.setFont(input->font());
1559 if (!qmlDisableDistanceField()) {
1561 option.setUseDesignMetrics(true);
1562 layout.setTextOption(option);
1564 layout.beginLayout();
1565 QTextLine line = layout.createLine();
1568 QCOMPARE(input->boundingRect().x(), qreal(0));
1569 QCOMPARE(input->boundingRect().y(), qreal(0));
1570 QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1571 QCOMPARE(input->boundingRect().height(), line.height());
1573 input->setText("Hello World");
1575 layout.setText(input->text());
1576 layout.beginLayout();
1577 line = layout.createLine();
1580 QCOMPARE(input->boundingRect().x(), qreal(0));
1581 QCOMPARE(input->boundingRect().y(), qreal(0));
1582 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1583 QCOMPARE(input->boundingRect().height(), line.height());
1585 // the size of the bounding rect shouldn't be bounded by the size of item.
1586 input->setWidth(input->width() / 2);
1587 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1588 QCOMPARE(input->boundingRect().y(), qreal(0));
1589 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1590 QCOMPARE(input->boundingRect().height(), line.height());
1592 input->setHeight(input->height() * 2);
1593 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1594 QCOMPARE(input->boundingRect().y(), qreal(0));
1595 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1596 QCOMPARE(input->boundingRect().height(), line.height());
1598 QQmlComponent cursorComponent(&engine);
1599 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1601 input->setCursorDelegate(&cursorComponent);
1602 input->setCursorVisible(true);
1604 // Don't include the size of a cursor delegate as it has its own bounding rect.
1605 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1606 QCOMPARE(input->boundingRect().y(), qreal(0));
1607 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1608 QCOMPARE(input->boundingRect().height(), line.height());
1610 // Bounding rect left aligned when auto scroll is disabled;
1611 input->setAutoScroll(false);
1612 QCOMPARE(input->boundingRect().x(), qreal(0));
1613 QCOMPARE(input->boundingRect().y(), qreal(0));
1614 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1615 QCOMPARE(input->boundingRect().height(), line.height());
1617 input->setHAlign(QQuickTextInput::AlignRight);
1618 QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1619 QCOMPARE(input->boundingRect().y(), qreal(0));
1620 QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1621 QCOMPARE(input->boundingRect().height(), line.height());
1623 input->setWrapMode(QQuickTextInput::Wrap);
1624 QCOMPARE(input->boundingRect().right(), input->width());
1625 QCOMPARE(input->boundingRect().y(), qreal(0));
1626 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1627 QVERIFY(input->boundingRect().height() > line.height());
1629 input->setVAlign(QQuickTextInput::AlignBottom);
1630 QCOMPARE(input->boundingRect().right(), input->width());
1631 QCOMPARE(input->boundingRect().bottom(), input->height());
1632 QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1633 QVERIFY(input->boundingRect().height() > line.height());
1636 void tst_qquicktextinput::positionAt()
1638 QQuickView canvas(testFileUrl("positionAt.qml"));
1639 QVERIFY(canvas.rootObject() != 0);
1641 canvas.requestActivateWindow();
1642 QTest::qWaitForWindowActive(&canvas);
1644 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1645 QVERIFY(textinputObject != 0);
1647 // Check autoscrolled...
1649 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1651 QTextLayout layout(textinputObject->text());
1652 layout.setFont(textinputObject->font());
1654 if (!qmlDisableDistanceField()) {
1656 option.setUseDesignMetrics(true);
1657 layout.setTextOption(option);
1659 layout.beginLayout();
1660 QTextLine line = layout.createLine();
1663 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1664 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1665 int textWidth = floor(line.horizontalAdvance());
1667 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1668 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1670 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1671 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1672 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1674 // Check without autoscroll...
1675 textinputObject->setAutoScroll(false);
1676 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1678 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1679 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1681 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1682 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1684 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1685 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1686 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1688 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1689 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1691 QString preeditText = textinputObject->text().mid(0, pos);
1692 textinputObject->setText(textinputObject->text().mid(pos));
1693 textinputObject->setCursorPosition(0);
1695 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1696 QVERIFY(qGuiApp->focusObject());
1697 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1699 // Check all points within the preedit text return the same position.
1700 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1701 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1702 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1704 // Verify positioning returns to normal after the preedit text.
1705 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1706 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1708 { QInputMethodEvent inputEvent;
1709 QVERIFY(qGuiApp->focusObject());
1710 QGuiApplication::sendEvent(textinputObject, &inputEvent); }
1713 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1715 const qreal y0 = line.height() / 2;
1716 const qreal y1 = line.height() * 3 / 2;
1718 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1719 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1721 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1722 QVERIFY(newLinePos > pos);
1723 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1726 void tst_qquicktextinput::maxLength()
1728 QQuickView canvas(testFileUrl("maxLength.qml"));
1729 QVERIFY(canvas.rootObject() != 0);
1731 canvas.requestActivateWindow();
1732 QTest::qWaitForWindowActive(&canvas);
1734 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1735 QVERIFY(textinputObject != 0);
1736 QVERIFY(textinputObject->text().isEmpty());
1737 QVERIFY(textinputObject->maxLength() == 10);
1738 foreach (const QString &str, standard) {
1739 QVERIFY(textinputObject->text().length() <= 10);
1740 textinputObject->setText(str);
1741 QVERIFY(textinputObject->text().length() <= 10);
1744 textinputObject->setText("");
1745 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1746 for (int i=0; i<20; i++) {
1747 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1748 //simulateKey(&canvas, Qt::Key_A);
1749 QTest::keyPress(&canvas, Qt::Key_A);
1750 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1755 void tst_qquicktextinput::masks()
1757 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1758 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1759 QQuickView canvas(testFileUrl("masks.qml"));
1761 canvas.requestActivateWindow();
1762 QVERIFY(canvas.rootObject() != 0);
1763 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1764 QVERIFY(textinputObject != 0);
1765 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1766 QVERIFY(textinputObject->text().length() == 0);
1767 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1768 QCOMPARE(textinputObject->length(), 8);
1769 for (int i=0; i<10; i++) {
1770 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1771 QCOMPARE(textinputObject->length(), 8);
1772 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1773 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1774 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1775 //simulateKey(&canvas, Qt::Key_A);
1776 QTest::keyPress(&canvas, Qt::Key_A);
1777 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1782 void tst_qquicktextinput::validators()
1784 // Note that this test assumes that the validators are working properly
1785 // so you may need to run their tests first. All validators are checked
1786 // here to ensure that their exposure to QML is working.
1788 QLocale::setDefault(QLocale(QStringLiteral("C")));
1790 QQuickView canvas(testFileUrl("validators.qml"));
1792 canvas.requestActivateWindow();
1793 QTest::qWaitForWindowActive(&canvas);
1795 QVERIFY(canvas.rootObject() != 0);
1797 QLocale defaultLocale;
1798 QLocale enLocale("en");
1799 QLocale deLocale("de_DE");
1801 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1803 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1805 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1806 QVERIFY(intValidator);
1807 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1808 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1809 intValidator->setLocaleName(enLocale.name());
1810 QCOMPARE(intValidator->localeName(), enLocale.name());
1811 QCOMPARE(intInput->validator()->locale(), enLocale);
1812 intValidator->resetLocaleName();
1813 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1814 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1816 intInput->setFocus(true);
1817 QTRY_VERIFY(intInput->hasActiveFocus());
1818 QCOMPARE(intInput->hasAcceptableInput(), false);
1819 QCOMPARE(intInput->property("acceptable").toBool(), false);
1820 QTest::keyPress(&canvas, Qt::Key_1);
1821 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1823 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1824 QCOMPARE(intInput->hasAcceptableInput(), false);
1825 QCOMPARE(intInput->property("acceptable").toBool(), false);
1826 QCOMPARE(intSpy.count(), 0);
1827 QTest::keyPress(&canvas, Qt::Key_2);
1828 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1830 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1831 QCOMPARE(intInput->hasAcceptableInput(), false);
1832 QCOMPARE(intInput->property("acceptable").toBool(), false);
1833 QCOMPARE(intSpy.count(), 0);
1834 QTest::keyPress(&canvas, Qt::Key_Period);
1835 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1837 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1838 QCOMPARE(intInput->hasAcceptableInput(), false);
1839 QTest::keyPress(&canvas, Qt::Key_Comma);
1840 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1842 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1843 QCOMPARE(intInput->hasAcceptableInput(), false);
1844 QTest::keyPress(&canvas, Qt::Key_Backspace);
1845 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1847 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1848 QCOMPARE(intInput->hasAcceptableInput(), false);
1849 intValidator->setLocaleName(deLocale.name());
1850 QTest::keyPress(&canvas, Qt::Key_Period);
1851 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1853 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1854 QCOMPARE(intInput->hasAcceptableInput(), false);
1855 QTest::keyPress(&canvas, Qt::Key_Backspace);
1856 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1858 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1859 QCOMPARE(intInput->hasAcceptableInput(), false);
1860 intValidator->resetLocaleName();
1861 QTest::keyPress(&canvas, Qt::Key_1);
1862 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1864 QCOMPARE(intInput->text(), QLatin1String("11"));
1865 QCOMPARE(intInput->hasAcceptableInput(), true);
1866 QCOMPARE(intInput->property("acceptable").toBool(), true);
1867 QCOMPARE(intSpy.count(), 1);
1868 QTest::keyPress(&canvas, Qt::Key_0);
1869 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1871 QCOMPARE(intInput->text(), QLatin1String("11"));
1872 QCOMPARE(intInput->hasAcceptableInput(), true);
1873 QCOMPARE(intInput->property("acceptable").toBool(), true);
1874 QCOMPARE(intSpy.count(), 1);
1876 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1878 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1880 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1881 QVERIFY(dblValidator);
1882 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1883 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1884 dblValidator->setLocaleName(enLocale.name());
1885 QCOMPARE(dblValidator->localeName(), enLocale.name());
1886 QCOMPARE(dblInput->validator()->locale(), enLocale);
1887 dblValidator->resetLocaleName();
1888 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1889 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1891 dblInput->setFocus(true);
1892 QVERIFY(dblInput->hasActiveFocus() == true);
1893 QCOMPARE(dblInput->hasAcceptableInput(), false);
1894 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1895 QTest::keyPress(&canvas, Qt::Key_1);
1896 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1898 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1899 QCOMPARE(dblInput->hasAcceptableInput(), false);
1900 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1901 QCOMPARE(dblSpy.count(), 0);
1902 QTest::keyPress(&canvas, Qt::Key_2);
1903 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1905 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1906 QCOMPARE(dblInput->hasAcceptableInput(), true);
1907 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1908 QCOMPARE(dblSpy.count(), 1);
1909 QTest::keyPress(&canvas, Qt::Key_Comma);
1910 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1912 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1913 QCOMPARE(dblInput->hasAcceptableInput(), true);
1914 QTest::keyPress(&canvas, Qt::Key_1);
1915 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1917 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1918 QCOMPARE(dblInput->hasAcceptableInput(), true);
1919 dblValidator->setLocaleName(deLocale.name());
1920 QCOMPARE(dblInput->hasAcceptableInput(), true);
1921 QTest::keyPress(&canvas, Qt::Key_1);
1922 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1924 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1925 QCOMPARE(dblInput->hasAcceptableInput(), true);
1926 QTest::keyPress(&canvas, Qt::Key_1);
1927 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1929 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1930 QCOMPARE(dblInput->hasAcceptableInput(), true);
1931 QTest::keyPress(&canvas, Qt::Key_Backspace);
1932 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1934 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1935 QCOMPARE(dblInput->hasAcceptableInput(), true);
1936 QTest::keyPress(&canvas, Qt::Key_Backspace);
1937 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1939 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1940 QCOMPARE(dblInput->hasAcceptableInput(), true);
1941 QTest::keyPress(&canvas, Qt::Key_Backspace);
1942 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1944 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1945 QCOMPARE(dblInput->hasAcceptableInput(), true);
1946 dblValidator->resetLocaleName();
1947 QTest::keyPress(&canvas, Qt::Key_Period);
1948 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1950 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1951 QCOMPARE(dblInput->hasAcceptableInput(), true);
1952 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1953 QCOMPARE(dblSpy.count(), 1);
1954 QTest::keyPress(&canvas, Qt::Key_1);
1955 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1957 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1958 QCOMPARE(dblInput->hasAcceptableInput(), true);
1959 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1960 QCOMPARE(dblSpy.count(), 1);
1961 QTest::keyPress(&canvas, Qt::Key_1);
1962 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1964 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1965 QCOMPARE(dblInput->hasAcceptableInput(), true);
1966 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1967 QCOMPARE(dblSpy.count(), 1);
1968 QTest::keyPress(&canvas, Qt::Key_1);
1969 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1971 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1972 QCOMPARE(dblInput->hasAcceptableInput(), true);
1973 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1974 QCOMPARE(dblSpy.count(), 1);
1976 // Ensure the validator doesn't prevent characters being removed.
1977 dblInput->setValidator(intInput->validator());
1978 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1979 QCOMPARE(dblInput->hasAcceptableInput(), false);
1980 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1981 QCOMPARE(dblSpy.count(), 2);
1982 QTest::keyPress(&canvas, Qt::Key_Backspace);
1983 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1985 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1986 QCOMPARE(dblInput->hasAcceptableInput(), false);
1987 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1988 QCOMPARE(dblSpy.count(), 2);
1989 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1990 QTest::keyPress(&canvas, Qt::Key_1);
1991 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1993 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1994 QCOMPARE(dblInput->hasAcceptableInput(), false);
1995 QCOMPARE(dblSpy.count(), 2);
1996 QTest::keyPress(&canvas, Qt::Key_Backspace);
1997 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1999 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2000 QCOMPARE(dblInput->hasAcceptableInput(), false);
2001 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2002 QCOMPARE(dblSpy.count(), 2);
2003 QTest::keyPress(&canvas, Qt::Key_Backspace);
2004 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2006 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2007 QCOMPARE(dblInput->hasAcceptableInput(), false);
2008 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2009 QCOMPARE(dblSpy.count(), 2);
2010 QTest::keyPress(&canvas, Qt::Key_Backspace);
2011 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2013 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2014 QCOMPARE(dblInput->hasAcceptableInput(), false);
2015 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2016 QCOMPARE(dblSpy.count(), 2);
2017 QTest::keyPress(&canvas, Qt::Key_Backspace);
2018 QTest::keyRelease(&canvas, Qt::Key_Backspace, 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(), 2);
2024 QTest::keyPress(&canvas, Qt::Key_1);
2025 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2027 QCOMPARE(dblInput->text(), QLatin1String("11"));
2028 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2029 QCOMPARE(dblInput->hasAcceptableInput(), true);
2030 QCOMPARE(dblSpy.count(), 3);
2032 // Changing the validator properties will re-evaluate whether the input is acceptable.
2033 intValidator->setTop(10);
2034 QCOMPARE(dblInput->property("acceptable").toBool(), false);
2035 QCOMPARE(dblInput->hasAcceptableInput(), false);
2036 QCOMPARE(dblSpy.count(), 4);
2037 intValidator->setTop(12);
2038 QCOMPARE(dblInput->property("acceptable").toBool(), true);
2039 QCOMPARE(dblInput->hasAcceptableInput(), true);
2040 QCOMPARE(dblSpy.count(), 5);
2042 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
2044 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2045 strInput->setFocus(true);
2046 QVERIFY(strInput->hasActiveFocus() == true);
2047 QCOMPARE(strInput->hasAcceptableInput(), false);
2048 QCOMPARE(strInput->property("acceptable").toBool(), false);
2049 QTest::keyPress(&canvas, Qt::Key_1);
2050 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2052 QTRY_COMPARE(strInput->text(), QLatin1String(""));
2053 QCOMPARE(strInput->hasAcceptableInput(), false);
2054 QCOMPARE(strInput->property("acceptable").toBool(), false);
2055 QCOMPARE(strSpy.count(), 0);
2056 QTest::keyPress(&canvas, Qt::Key_A);
2057 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2059 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2060 QCOMPARE(strInput->hasAcceptableInput(), false);
2061 QCOMPARE(strInput->property("acceptable").toBool(), false);
2062 QCOMPARE(strSpy.count(), 0);
2063 QTest::keyPress(&canvas, Qt::Key_A);
2064 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2066 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2067 QCOMPARE(strInput->hasAcceptableInput(), true);
2068 QCOMPARE(strInput->property("acceptable").toBool(), true);
2069 QCOMPARE(strSpy.count(), 1);
2070 QTest::keyPress(&canvas, Qt::Key_A);
2071 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2073 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2074 QCOMPARE(strInput->hasAcceptableInput(), true);
2075 QCOMPARE(strInput->property("acceptable").toBool(), true);
2076 QCOMPARE(strSpy.count(), 1);
2077 QTest::keyPress(&canvas, Qt::Key_A);
2078 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2080 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2081 QCOMPARE(strInput->hasAcceptableInput(), true);
2082 QCOMPARE(strInput->property("acceptable").toBool(), true);
2083 QCOMPARE(strSpy.count(), 1);
2084 QTest::keyPress(&canvas, Qt::Key_A);
2085 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2087 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2088 QCOMPARE(strInput->hasAcceptableInput(), true);
2089 QCOMPARE(strInput->property("acceptable").toBool(), true);
2090 QCOMPARE(strSpy.count(), 1);
2092 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
2093 QVERIFY(unvalidatedInput);
2094 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2095 unvalidatedInput->setFocus(true);
2096 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2097 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2098 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2099 QTest::keyPress(&canvas, Qt::Key_1);
2100 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2102 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2103 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2104 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2105 QCOMPARE(unvalidatedSpy.count(), 0);
2106 QTest::keyPress(&canvas, Qt::Key_A);
2107 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2109 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2110 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2111 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2112 QCOMPARE(unvalidatedSpy.count(), 0);
2115 void tst_qquicktextinput::inputMethods()
2117 QQuickView canvas(testFileUrl("inputmethods.qml"));
2119 canvas.requestActivateWindow();
2120 QTest::qWaitForWindowActive(&canvas);
2122 // test input method hints
2123 QVERIFY(canvas.rootObject() != 0);
2124 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
2125 QVERIFY(input != 0);
2126 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2127 QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2128 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2129 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2130 QCOMPARE(inputMethodHintSpy.count(), 1);
2131 input->setInputMethodHints(Qt::ImhUppercaseOnly);
2132 QCOMPARE(inputMethodHintSpy.count(), 1);
2135 QQuickTextInput plainInput;
2136 QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2138 input->setFocus(true);
2139 QVERIFY(input->hasActiveFocus() == true);
2140 // test that input method event is committed
2141 QInputMethodEvent event;
2142 event.setCommitString( "My ", -12, 0);
2143 QTRY_COMPARE(qGuiApp->focusObject(), input);
2144 QGuiApplication::sendEvent(input, &event);
2145 QCOMPARE(input->text(), QString("My Hello world!"));
2147 input->setCursorPosition(2);
2148 event.setCommitString("Your", -2, 2);
2149 QGuiApplication::sendEvent(input, &event);
2150 QCOMPARE(input->text(), QString("Your Hello world!"));
2151 QCOMPARE(input->cursorPosition(), 4);
2153 input->setCursorPosition(7);
2154 event.setCommitString("Goodbye", -2, 5);
2155 QGuiApplication::sendEvent(input, &event);
2156 QCOMPARE(input->text(), QString("Your Goodbye world!"));
2157 QCOMPARE(input->cursorPosition(), 12);
2159 input->setCursorPosition(8);
2160 event.setCommitString("Our", -8, 4);
2161 QGuiApplication::sendEvent(input, &event);
2162 QCOMPARE(input->text(), QString("Our Goodbye world!"));
2163 QCOMPARE(input->cursorPosition(), 7);
2165 // input should reset selection even if replacement parameters are out of bounds
2166 input->setText("text");
2167 input->setCursorPosition(0);
2168 input->moveCursorSelection(input->text().length());
2169 event.setCommitString("replacement", -input->text().length(), input->text().length());
2170 QGuiApplication::sendEvent(input, &event);
2171 QCOMPARE(input->selectionStart(), input->selectionEnd());
2173 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2174 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2175 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2177 input->setReadOnly(true);
2178 QGuiApplication::sendEvent(input, &enabledQueryEvent);
2179 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2183 TextInput element should only handle left/right keys until the cursor reaches
2184 the extent of the text, then they should ignore the keys.
2187 void tst_qquicktextinput::navigation()
2189 QQuickView canvas(testFileUrl("navigation.qml"));
2191 canvas.requestActivateWindow();
2193 QVERIFY(canvas.rootObject() != 0);
2195 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2197 QVERIFY(input != 0);
2198 input->setCursorPosition(0);
2199 QTRY_VERIFY(input->hasActiveFocus() == true);
2200 simulateKey(&canvas, Qt::Key_Left);
2201 QVERIFY(input->hasActiveFocus() == false);
2202 simulateKey(&canvas, Qt::Key_Right);
2203 QVERIFY(input->hasActiveFocus() == true);
2204 //QT-2944: If text is selected, ensure we deselect upon cursor motion
2205 input->setCursorPosition(input->text().length());
2206 input->select(0,input->text().length());
2207 QVERIFY(input->selectionStart() != input->selectionEnd());
2208 simulateKey(&canvas, Qt::Key_Right);
2209 QVERIFY(input->selectionStart() == input->selectionEnd());
2210 QVERIFY(input->selectionStart() == input->text().length());
2211 QVERIFY(input->hasActiveFocus() == true);
2212 simulateKey(&canvas, Qt::Key_Right);
2213 QVERIFY(input->hasActiveFocus() == false);
2214 simulateKey(&canvas, Qt::Key_Left);
2215 QVERIFY(input->hasActiveFocus() == true);
2217 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2218 input->setCursorPosition(2);
2219 QCOMPARE(input->cursorPosition(),2);
2220 simulateKey(&canvas, Qt::Key_Up);
2221 QCOMPARE(input->cursorPosition(),2);
2222 simulateKey(&canvas, Qt::Key_Down);
2223 QCOMPARE(input->cursorPosition(),2);
2225 // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2226 input->setText(QString());
2227 QCOMPARE(input->cursorPosition(), 0);
2228 simulateKey(&canvas, Qt::Key_Right);
2229 QCOMPARE(input->hasActiveFocus(), false);
2230 simulateKey(&canvas, Qt::Key_Left);
2231 QCOMPARE(input->hasActiveFocus(), true);
2232 simulateKey(&canvas, Qt::Key_Left);
2233 QCOMPARE(input->hasActiveFocus(), false);
2236 void tst_qquicktextinput::navigation_RTL()
2238 QQuickView canvas(testFileUrl("navigation.qml"));
2240 canvas.requestActivateWindow();
2242 QVERIFY(canvas.rootObject() != 0);
2244 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2246 QVERIFY(input != 0);
2247 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2248 input->setText(QString::fromUtf16(arabic_str, 11));
2250 input->setCursorPosition(0);
2251 QTRY_VERIFY(input->hasActiveFocus() == true);
2254 simulateKey(&canvas, Qt::Key_Right);
2255 QVERIFY(input->hasActiveFocus() == false);
2258 simulateKey(&canvas, Qt::Key_Left);
2259 QVERIFY(input->hasActiveFocus() == true);
2261 input->setCursorPosition(input->text().length());
2262 QVERIFY(input->hasActiveFocus() == true);
2265 simulateKey(&canvas, Qt::Key_Left);
2266 QVERIFY(input->hasActiveFocus() == false);
2269 simulateKey(&canvas, Qt::Key_Right);
2270 QVERIFY(input->hasActiveFocus() == true);
2273 void tst_qquicktextinput::copyAndPaste() {
2274 #ifndef QT_NO_CLIPBOARD
2278 PasteboardRef pasteboard;
2279 OSStatus status = PasteboardCreate(0, &pasteboard);
2280 if (status == noErr)
2281 CFRelease(pasteboard);
2283 QSKIP("This machine doesn't support the clipboard");
2287 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2288 QQmlComponent textInputComponent(&engine);
2289 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2290 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2291 QVERIFY(textInput != 0);
2294 QCOMPARE(textInput->text().length(), 12);
2295 textInput->select(0, textInput->text().length());
2297 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2298 QCOMPARE(textInput->selectedText().length(), 12);
2299 textInput->setCursorPosition(0);
2300 QVERIFY(textInput->canPaste());
2302 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2303 QCOMPARE(textInput->text().length(), 24);
2306 QVERIFY(textInput->canPaste());
2307 textInput->setReadOnly(true);
2308 QVERIFY(!textInput->canPaste());
2309 textInput->setReadOnly(false);
2310 QVERIFY(textInput->canPaste());
2313 textInput->setCursorPosition(0);
2314 textInput->selectWord();
2315 QCOMPARE(textInput->selectedText(), QString("Hello"));
2317 // select all and cut
2318 textInput->selectAll();
2320 QCOMPARE(textInput->text().length(), 0);
2322 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2323 QCOMPARE(textInput->text().length(), 24);
2325 // clear copy buffer
2326 QClipboard *clipboard = QGuiApplication::clipboard();
2329 QVERIFY(!textInput->canPaste());
2331 // test that copy functionality is disabled
2332 // when echo mode is set to hide text/password mode
2335 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2336 textInput->setEchoMode(echoMode);
2337 textInput->setText("My password");
2338 textInput->select(0, textInput->text().length());
2340 if (echoMode == QQuickTextInput::Normal) {
2341 QVERIFY(!clipboard->text().isEmpty());
2342 QCOMPARE(clipboard->text(), QString("My password"));
2345 QVERIFY(clipboard->text().isEmpty());
2354 void tst_qquicktextinput::copyAndPasteKeySequence() {
2355 #ifndef QT_NO_CLIPBOARD
2359 PasteboardRef pasteboard;
2360 OSStatus status = PasteboardCreate(0, &pasteboard);
2361 if (status == noErr)
2362 CFRelease(pasteboard);
2364 QSKIP("This machine doesn't support the clipboard");
2368 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2369 QQmlComponent textInputComponent(&engine);
2370 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2371 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2372 QVERIFY(textInput != 0);
2374 QQuickCanvas canvas;
2375 textInput->setParentItem(canvas.rootItem());
2377 canvas.requestActivateWindow();
2378 QTest::qWaitForWindowActive(&canvas);
2381 QVERIFY(textInput->hasActiveFocus());
2382 QCOMPARE(textInput->text().length(), 12);
2383 textInput->select(0, textInput->text().length());
2384 simulateKeys(&canvas, QKeySequence::Copy);
2385 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2386 QCOMPARE(textInput->selectedText().length(), 12);
2387 textInput->setCursorPosition(0);
2388 QVERIFY(textInput->canPaste());
2389 simulateKeys(&canvas, QKeySequence::Paste);
2390 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2391 QCOMPARE(textInput->text().length(), 24);
2393 // select all and cut
2394 simulateKeys(&canvas, QKeySequence::SelectAll);
2395 simulateKeys(&canvas, QKeySequence::Cut);
2396 QCOMPARE(textInput->text().length(), 0);
2397 simulateKeys(&canvas, QKeySequence::Paste);
2398 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2399 QCOMPARE(textInput->text().length(), 24);
2401 // clear copy buffer
2402 QClipboard *clipboard = QGuiApplication::clipboard();
2405 QVERIFY(!textInput->canPaste());
2407 // test that copy functionality is disabled
2408 // when echo mode is set to hide text/password mode
2411 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2412 textInput->setEchoMode(echoMode);
2413 textInput->setText("My password");
2414 textInput->select(0, textInput->text().length());
2415 simulateKeys(&canvas, QKeySequence::Copy);
2416 if (echoMode == QQuickTextInput::Normal) {
2417 QVERIFY(!clipboard->text().isEmpty());
2418 QCOMPARE(clipboard->text(), QString("My password"));
2421 QVERIFY(clipboard->text().isEmpty());
2430 void tst_qquicktextinput::canPasteEmpty() {
2431 #ifndef QT_NO_CLIPBOARD
2433 QGuiApplication::clipboard()->clear();
2435 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2436 QQmlComponent textInputComponent(&engine);
2437 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2438 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2439 QVERIFY(textInput != 0);
2441 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2442 QCOMPARE(textInput->canPaste(), cp);
2447 void tst_qquicktextinput::canPaste() {
2448 #ifndef QT_NO_CLIPBOARD
2450 QGuiApplication::clipboard()->setText("Some text");
2452 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2453 QQmlComponent textInputComponent(&engine);
2454 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2455 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2456 QVERIFY(textInput != 0);
2458 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2459 QCOMPARE(textInput->canPaste(), cp);
2464 void tst_qquicktextinput::passwordCharacter()
2466 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2467 QQmlComponent textInputComponent(&engine);
2468 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2469 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2470 QVERIFY(textInput != 0);
2472 textInput->setPasswordCharacter("X");
2473 qreal implicitWidth = textInput->implicitWidth();
2474 textInput->setPasswordCharacter(".");
2476 // QTBUG-12383 content is updated and redrawn
2477 QVERIFY(textInput->implicitWidth() < implicitWidth);
2482 void tst_qquicktextinput::cursorDelegate_data()
2484 QTest::addColumn<QUrl>("source");
2485 QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2486 QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2487 QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2490 void tst_qquicktextinput::cursorDelegate()
2492 QFETCH(QUrl, source);
2493 QQuickView view(source);
2495 view.requestActivateWindow();
2496 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2497 QVERIFY(textInputObject != 0);
2498 // Delegate is created on demand, and so won't be available immediately. Focus in or
2499 // setCursorVisible(true) will trigger creation.
2500 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2501 QVERIFY(!textInputObject->isCursorVisible());
2502 //Test Delegate gets created
2503 textInputObject->setFocus(true);
2504 QVERIFY(textInputObject->isCursorVisible());
2505 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2506 QVERIFY(delegateObject);
2507 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2508 //Test Delegate gets moved
2509 for (int i=0; i<= textInputObject->text().length(); i++) {
2510 textInputObject->setCursorPosition(i);
2511 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2512 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2514 textInputObject->setCursorPosition(0);
2515 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2516 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2518 // Test delegate gets moved on mouse press.
2519 textInputObject->setSelectByMouse(true);
2520 textInputObject->setCursorPosition(0);
2521 const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2522 QTest::qWait(400); //ensure this isn't treated as a double-click
2523 QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2525 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2526 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2527 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2529 // Test delegate gets moved on mouse drag
2530 textInputObject->setCursorPosition(0);
2531 const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2532 QTest::qWait(400); //ensure this isn't treated as a double-click
2533 QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2534 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2535 QGuiApplication::sendEvent(&view, &mv);
2536 QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2538 QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2539 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2541 textInputObject->setReadOnly(true);
2542 textInputObject->setCursorPosition(0);
2543 QTest::qWait(400); //ensure this isn't treated as a double-click
2544 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2546 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2547 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2548 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2550 textInputObject->setCursorPosition(0);
2551 QTest::qWait(400); //ensure this isn't treated as a double-click
2552 QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2554 QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2555 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2556 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2558 textInputObject->setCursorPosition(0);
2559 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2560 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2562 textInputObject->setReadOnly(false);
2564 // Delegate moved when text is entered
2565 textInputObject->setText(QString());
2566 for (int i = 0; i < 20; ++i) {
2567 QTest::keyClick(&view, Qt::Key_A);
2568 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2569 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2572 // Delegate moved when text is entered by im.
2573 textInputObject->setText(QString());
2574 for (int i = 0; i < 20; ++i) {
2575 QInputMethodEvent event;
2576 event.setCommitString("w");
2577 QGuiApplication::sendEvent(textInputObject, &event);
2578 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2579 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2581 // Delegate moved when text is removed by im.
2582 for (int i = 19; i > 1; --i) {
2583 QInputMethodEvent event;
2584 event.setCommitString(QString(), -1, 1);
2585 QGuiApplication::sendEvent(textInputObject, &event);
2586 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2587 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2589 { // Delegate moved the text is changed in place by im.
2590 QInputMethodEvent event;
2591 event.setCommitString("i", -1, 1);
2592 QGuiApplication::sendEvent(textInputObject, &event);
2593 QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2594 QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2597 //Test Delegate gets deleted
2598 textInputObject->setCursorDelegate(0);
2599 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2602 void tst_qquicktextinput::remoteCursorDelegate()
2604 TestHTTPServer server(SERVER_PORT);
2605 server.serveDirectory(dataDirectory());
2609 QQmlComponent component(view.engine(), QUrl(SERVER_ADDR "/RemoteCursor.qml"));
2611 view.rootContext()->setContextProperty("contextDelegate", &component);
2612 view.setSource(testFileUrl("cursorTestRemote.qml"));
2614 view.requestActivateWindow();
2615 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2616 QVERIFY(textInputObject != 0);
2618 // Delegate is created on demand, and so won't be available immediately. Focus in or
2619 // setCursorVisible(true) will trigger creation.
2620 QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2621 QVERIFY(!textInputObject->isCursorVisible());
2623 textInputObject->setFocus(true);
2624 QVERIFY(textInputObject->isCursorVisible());
2626 QCOMPARE(component.status(), QQmlComponent::Loading);
2627 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2629 // Wait for component to load.
2630 QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2631 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2634 void tst_qquicktextinput::cursorVisible()
2636 QQuickTextInput input;
2637 input.componentComplete();
2638 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2640 QQuickView view(testFileUrl("cursorVisible.qml"));
2642 view.requestActivateWindow();
2643 QTest::qWaitForWindowActive(&view);
2645 QCOMPARE(input.isCursorVisible(), false);
2647 input.setCursorVisible(true);
2648 QCOMPARE(input.isCursorVisible(), true);
2649 QCOMPARE(spy.count(), 1);
2651 input.setCursorVisible(false);
2652 QCOMPARE(input.isCursorVisible(), false);
2653 QCOMPARE(spy.count(), 2);
2655 input.setFocus(true);
2656 QCOMPARE(input.isCursorVisible(), false);
2657 QCOMPARE(spy.count(), 2);
2659 input.setParentItem(view.rootObject());
2660 QCOMPARE(input.isCursorVisible(), true);
2661 QCOMPARE(spy.count(), 3);
2663 input.setFocus(false);
2664 QCOMPARE(input.isCursorVisible(), false);
2665 QCOMPARE(spy.count(), 4);
2667 input.setFocus(true);
2668 QCOMPARE(input.isCursorVisible(), true);
2669 QCOMPARE(spy.count(), 5);
2671 QWindow alternateView;
2672 alternateView.show();
2673 alternateView.requestActivateWindow();
2674 QTest::qWaitForWindowActive(&alternateView);
2676 QCOMPARE(input.isCursorVisible(), false);
2677 QCOMPARE(spy.count(), 6);
2679 view.requestActivateWindow();
2680 QTest::qWaitForWindowActive(&view);
2681 QCOMPARE(input.isCursorVisible(), true);
2682 QCOMPARE(spy.count(), 7);
2684 { // Cursor attribute with 0 length hides cursor.
2685 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2686 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2687 QCoreApplication::sendEvent(&input, &ev);
2689 QCOMPARE(input.isCursorVisible(), false);
2690 QCOMPARE(spy.count(), 8);
2692 { // Cursor attribute with non zero length shows cursor.
2693 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2694 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2695 QCoreApplication::sendEvent(&input, &ev);
2697 QCOMPARE(input.isCursorVisible(), true);
2698 QCOMPARE(spy.count(), 9);
2700 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2701 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2702 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2703 QCoreApplication::sendEvent(&input, &ev);
2705 QCOMPARE(input.isCursorVisible(), false);
2706 QCOMPARE(spy.count(), 10);
2708 input.setText("something");
2709 QCOMPARE(input.isCursorVisible(), true);
2710 QCOMPARE(spy.count(), 11);
2712 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2713 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2714 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2715 QCoreApplication::sendEvent(&input, &ev);
2717 QCOMPARE(input.isCursorVisible(), false);
2718 QCOMPARE(spy.count(), 12);
2720 input.setCursorPosition(5);
2721 QCOMPARE(input.isCursorVisible(), true);
2722 QCOMPARE(spy.count(), 13);
2725 void tst_qquicktextinput::cursorRectangle_data()
2727 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2729 QTest::addColumn<QString>("text");
2730 QTest::addColumn<int>("positionAtWidth");
2731 QTest::addColumn<int>("wrapPosition");
2732 QTest::addColumn<QString>("shortText");
2733 QTest::addColumn<bool>("leftToRight");
2735 QTest::newRow("left to right")
2736 << "Hello World!" << 5 << 11
2739 QTest::newRow("right to left")
2740 << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2741 << QString::fromUtf16(arabic_str, 3)
2745 void tst_qquicktextinput::cursorRectangle()
2747 QFETCH(QString, text);
2748 QFETCH(int, positionAtWidth);
2749 QFETCH(int, wrapPosition);
2750 QFETCH(QString, shortText);
2751 QFETCH(bool, leftToRight);
2753 QQuickTextInput input;
2754 input.setText(text);
2755 input.componentComplete();
2757 QTextLayout layout(text);
2758 layout.setFont(input.font());
2759 if (!qmlDisableDistanceField()) {
2761 option.setUseDesignMetrics(true);
2762 layout.setTextOption(option);
2764 layout.beginLayout();
2765 QTextLine line = layout.createLine();
2770 input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2772 input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2773 offset = line.horizontalAdvance() - input.width();
2775 input.setHeight(qCeil(line.height() * 3 / 2));
2779 for (int i = 0; i <= positionAtWidth; ++i) {
2780 input.setCursorPosition(i);
2781 r = input.cursorRectangle();
2783 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2784 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2785 QCOMPARE(input.positionToRectangle(i), r);
2788 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2789 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2791 for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2792 input.setCursorPosition(i);
2793 QCOMPARE(r, input.cursorRectangle());
2794 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2795 QCOMPARE(input.positionToRectangle(i), r);
2798 for (int i = text.length() - 2; i >= 0; --i) {
2799 input.setCursorPosition(i);
2800 r = input.cursorRectangle();
2801 QCOMPARE(r.top(), 0.);
2803 QVERIFY(r.right() >= 0);
2805 QVERIFY(r.left() <= input.width());
2807 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2808 QCOMPARE(input.positionToRectangle(i), r);
2811 // Check position with word wrap.
2812 input.setWrapMode(QQuickTextInput::WordWrap);
2813 input.setAutoScroll(false);
2814 for (int i = 0; i < wrapPosition; ++i) {
2815 input.setCursorPosition(i);
2816 r = input.cursorRectangle();
2818 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2819 QCOMPARE(r.top(), 0.);
2820 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2821 QCOMPARE(input.positionToRectangle(i), r);
2824 input.setCursorPosition(wrapPosition);
2825 r = input.cursorRectangle();
2827 QCOMPARE(r.left(), 0.);
2829 QCOMPARE(r.left(), input.width());
2831 QVERIFY(r.top() >= line.height() - 1);
2832 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2833 QCOMPARE(input.positionToRectangle(11), r);
2835 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2836 input.setCursorPosition(i);
2837 r = input.cursorRectangle();
2838 QVERIFY(r.top() >= line.height() - 1);
2839 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2840 QCOMPARE(input.positionToRectangle(i), r);
2843 // Check vertical scrolling with word wrap.
2844 input.setAutoScroll(true);
2845 for (int i = 0; i <= positionAtWidth; ++i) {
2846 input.setCursorPosition(i);
2847 r = input.cursorRectangle();
2849 QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2850 QCOMPARE(r.top(), 0.);
2851 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2852 QCOMPARE(input.positionToRectangle(i), r);
2855 // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
2856 // reaches the next non-whitespace character.
2857 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2858 for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
2859 input.setCursorPosition(i);
2860 QCOMPARE(r, input.cursorRectangle());
2861 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2862 QCOMPARE(input.positionToRectangle(i), r);
2865 input.setCursorPosition(wrapPosition);
2866 r = input.cursorRectangle();
2868 QCOMPARE(r.left(), 0.);
2870 QCOMPARE(r.left(), input.width());
2872 QVERIFY(r.bottom() >= input.height());
2873 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2874 QCOMPARE(input.positionToRectangle(11), r);
2876 for (int i = wrapPosition + 1; i < text.length(); ++i) {
2877 input.setCursorPosition(i);
2878 r = input.cursorRectangle();
2879 QVERIFY(r.bottom() >= input.height());
2880 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2881 QCOMPARE(input.positionToRectangle(i), r);
2884 for (int i = text.length() - 2; i >= wrapPosition; --i) {
2885 input.setCursorPosition(i);
2886 r = input.cursorRectangle();
2887 QVERIFY(r.bottom() >= input.height());
2888 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2889 QCOMPARE(input.positionToRectangle(i), r);
2892 input.setCursorPosition(wrapPosition - 1);
2893 r = input.cursorRectangle();
2894 QCOMPARE(r.top(), 0.);
2895 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2896 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2897 QCOMPARE(input.positionToRectangle(10), r);
2899 for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
2900 input.setCursorPosition(i);
2901 QCOMPARE(r, input.cursorRectangle());
2902 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2903 QCOMPARE(input.positionToRectangle(i), r);
2906 for (int i = positionAtWidth; i >= 0; --i) {
2907 input.setCursorPosition(i);
2908 r = input.cursorRectangle();
2909 QCOMPARE(r.top(), 0.);
2910 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2911 QCOMPARE(input.positionToRectangle(i), r);
2914 input.setText(shortText);
2915 input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
2916 r = input.cursorRectangle();
2917 QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2920 void tst_qquicktextinput::readOnly()
2922 QQuickView canvas(testFileUrl("readOnly.qml"));
2924 canvas.requestActivateWindow();
2926 QVERIFY(canvas.rootObject() != 0);
2928 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2930 QVERIFY(input != 0);
2931 QTRY_VERIFY(input->hasActiveFocus() == true);
2932 QVERIFY(input->isReadOnly() == true);
2933 QString initial = input->text();
2934 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2935 simulateKey(&canvas, k);
2936 simulateKey(&canvas, Qt::Key_Return);
2937 simulateKey(&canvas, Qt::Key_Space);
2938 simulateKey(&canvas, Qt::Key_Escape);
2939 QCOMPARE(input->text(), initial);
2941 input->setCursorPosition(3);
2942 input->setReadOnly(false);
2943 QCOMPARE(input->isReadOnly(), false);
2944 QCOMPARE(input->cursorPosition(), input->text().length());
2947 void tst_qquicktextinput::echoMode()
2949 QQuickView canvas(testFileUrl("echoMode.qml"));
2951 canvas.requestActivateWindow();
2952 QTest::qWaitForWindowActive(&canvas);
2954 QVERIFY(canvas.rootObject() != 0);
2956 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2958 QVERIFY(input != 0);
2959 QTRY_VERIFY(input->hasActiveFocus() == true);
2960 QString initial = input->text();
2961 Qt::InputMethodHints ref;
2962 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2963 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2964 QCOMPARE(input->displayText(), input->text());
2966 ref &= ~Qt::ImhHiddenText;
2967 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2968 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2969 input->setEchoMode(QQuickTextInput::NoEcho);
2970 QCOMPARE(input->text(), initial);
2971 QCOMPARE(input->displayText(), QLatin1String(""));
2972 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2974 ref |= Qt::ImhHiddenText;
2975 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2976 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2977 input->setEchoMode(QQuickTextInput::Password);
2979 ref |= Qt::ImhHiddenText;
2980 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2981 QCOMPARE(input->text(), initial);
2982 QCOMPARE(input->displayText(), QLatin1String("********"));
2983 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2984 // clearing input hints do not clear bits set by echo mode
2985 input->setInputMethodHints(Qt::ImhNone);
2986 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2987 input->setPasswordCharacter(QChar('Q'));
2988 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2989 QCOMPARE(input->text(), initial);
2990 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2991 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2992 //PasswordEchoOnEdit
2993 ref &= ~Qt::ImhHiddenText;
2994 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2995 QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2996 QCOMPARE(input->text(), initial);
2997 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2998 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2999 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
3000 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
3001 QCOMPARE(input->text(), QLatin1String("a"));
3002 QCOMPARE(input->displayText(), QLatin1String("a"));
3003 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
3004 input->setFocus(false);
3005 QVERIFY(input->hasActiveFocus() == false);
3006 QCOMPARE(input->displayText(), QLatin1String("Q"));
3007 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
3008 input->setFocus(true);
3009 QVERIFY(input->hasActiveFocus());
3010 QInputMethodEvent inputEvent;
3011 inputEvent.setCommitString(initial);
3012 QGuiApplication::sendEvent(input, &inputEvent);
3013 QCOMPARE(input->text(), initial);
3014 QCOMPARE(input->displayText(), initial);
3015 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
3018 void tst_qquicktextinput::passwordEchoDelay()
3020 int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
3022 QSKIP("No mask delay in use");
3023 QQuickView canvas(testFileUrl("echoMode.qml"));
3025 canvas.requestActivateWindow();
3026 QTest::qWaitForWindowActive(&canvas);
3028 QVERIFY(canvas.rootObject() != 0);
3030 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
3032 QVERIFY(input->hasActiveFocus());
3034 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3037 QChar fillChar = QLatin1Char('*');
3039 input->setEchoMode(QQuickTextInput::Password);
3040 QCOMPARE(input->displayText(), QString(8, fillChar));
3041 input->setText(QString());
3042 QCOMPARE(input->displayText(), QString());
3044 QTest::keyPress(&canvas, '0');
3045 QTest::keyPress(&canvas, '1');
3046 QTest::keyPress(&canvas, '2');
3047 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
3048 QTest::keyPress(&canvas, '3');
3049 QTest::keyPress(&canvas, '4');
3050 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3051 QTest::keyPress(&canvas, Qt::Key_Backspace);
3052 QCOMPARE(input->displayText(), QString(4, fillChar));
3053 QTest::keyPress(&canvas, '4');
3054 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3055 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3057 // Verify the last character entered is replaced by the fill character after a delay.
3058 // Also check the cursor position is updated to accomdate a size difference between
3059 // the fill character and the replaced character.
3060 QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged()));
3061 QTest::qWait(maskDelay);
3062 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
3063 QCOMPARE(cursorSpy.count(), 1);
3064 QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3066 QTest::keyPress(&canvas, '5');
3067 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3068 input->setFocus(false);
3069 QVERIFY(!input->hasFocus());
3070 QCOMPARE(input->displayText(), QString(6, fillChar));
3071 input->setFocus(true);
3072 QTRY_VERIFY(input->hasFocus());
3073 QCOMPARE(input->displayText(), QString(6, fillChar));
3074 QTest::keyPress(&canvas, '6');
3075 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3077 QInputMethodEvent ev;
3078 ev.setCommitString(QLatin1String("7"));
3079 QGuiApplication::sendEvent(input, &ev);
3080 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3082 input->setCursorPosition(3);
3083 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3084 QTest::keyPress(&canvas, 'a');
3085 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3086 QTest::keyPress(&canvas, Qt::Key_Backspace);
3087 QCOMPARE(input->displayText(), QString(8, fillChar));
3091 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3093 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3094 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3096 QGuiApplication::sendEvent(view, &press);
3097 QGuiApplication::sendEvent(view, &release);
3101 void tst_qquicktextinput::focusOnPress()
3103 QString componentStr =
3104 "import QtQuick 2.0\n"
3106 "property bool selectOnFocus: false\n"
3107 "width: 100; height: 50\n"
3108 "activeFocusOnPress: true\n"
3109 "text: \"Hello World\"\n"
3110 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3112 QQmlComponent texteditComponent(&engine);
3113 texteditComponent.setData(componentStr.toLatin1(), QUrl());
3114 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3115 QVERIFY(textInputObject != 0);
3116 QCOMPARE(textInputObject->focusOnPress(), true);
3117 QCOMPARE(textInputObject->hasFocus(), false);
3119 QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3120 QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3121 QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3123 textInputObject->setFocusOnPress(true);
3124 QCOMPARE(textInputObject->focusOnPress(), true);
3125 QCOMPARE(activeFocusOnPressSpy.count(), 0);
3127 QQuickCanvas canvas;
3128 canvas.resize(100, 50);
3129 textInputObject->setParentItem(canvas.rootItem());
3131 canvas.requestActivateWindow();
3132 QTest::qWaitForWindowActive(&canvas);
3134 QCOMPARE(textInputObject->hasFocus(), false);
3135 QCOMPARE(textInputObject->hasActiveFocus(), false);
3137 QPoint centerPoint(canvas.width()/2, canvas.height()/2);
3138 Qt::KeyboardModifiers noModifiers = 0;
3139 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3140 QGuiApplication::processEvents();
3141 QCOMPARE(textInputObject->hasFocus(), true);
3142 QCOMPARE(textInputObject->hasActiveFocus(), true);
3143 QCOMPARE(focusSpy.count(), 1);
3144 QCOMPARE(activeFocusSpy.count(), 1);
3145 QCOMPARE(textInputObject->selectedText(), QString());
3146 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3148 textInputObject->setFocusOnPress(false);
3149 QCOMPARE(textInputObject->focusOnPress(), false);
3150 QCOMPARE(activeFocusOnPressSpy.count(), 1);
3152 textInputObject->setFocus(false);
3153 QCOMPARE(textInputObject->hasFocus(), false);
3154 QCOMPARE(textInputObject->hasActiveFocus(), false);
3155 QCOMPARE(focusSpy.count(), 2);
3156 QCOMPARE(activeFocusSpy.count(), 2);
3158 // Wait for double click timeout to expire before clicking again.
3160 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3161 QGuiApplication::processEvents();
3162 QCOMPARE(textInputObject->hasFocus(), false);
3163 QCOMPARE(textInputObject->hasActiveFocus(), false);
3164 QCOMPARE(focusSpy.count(), 2);
3165 QCOMPARE(activeFocusSpy.count(), 2);
3166 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3168 textInputObject->setFocusOnPress(true);
3169 QCOMPARE(textInputObject->focusOnPress(), true);
3170 QCOMPARE(activeFocusOnPressSpy.count(), 2);
3172 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3173 textInputObject->setProperty("selectOnFocus", true);
3176 QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3177 QGuiApplication::processEvents();
3178 QCOMPARE(textInputObject->hasFocus(), true);
3179 QCOMPARE(textInputObject->hasActiveFocus(), true);
3180 QCOMPARE(focusSpy.count(), 3);
3181 QCOMPARE(activeFocusSpy.count(), 3);
3182 QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3183 QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3186 void tst_qquicktextinput::openInputPanel()
3188 PlatformInputContext platformInputContext;
3189 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3190 inputMethodPrivate->testContext = &platformInputContext;
3192 QQuickView view(testFileUrl("openInputPanel.qml"));
3194 view.requestActivateWindow();
3195 QTest::qWaitForWindowActive(&view);
3197 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3200 // check default values
3201 QVERIFY(input->focusOnPress());
3202 QVERIFY(!input->hasActiveFocus());
3203 QVERIFY(qApp->focusObject() != input);
3204 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3206 // input panel should open on focus
3207 QPoint centerPoint(view.width()/2, view.height()/2);
3208 Qt::KeyboardModifiers noModifiers = 0;
3209 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3210 QGuiApplication::processEvents();
3211 QVERIFY(input->hasActiveFocus());
3212 QCOMPARE(qApp->focusObject(), input);
3213 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3214 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3216 // input panel should be re-opened when pressing already focused TextInput
3217 qApp->inputMethod()->hide();
3218 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3219 QVERIFY(input->hasActiveFocus());
3220 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3221 QGuiApplication::processEvents();
3222 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3223 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3225 // input panel should stay visible if focus is lost to another text inputor
3226 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3227 QQuickTextInput anotherInput;
3228 anotherInput.componentComplete();
3229 anotherInput.setParentItem(view.rootObject());
3230 anotherInput.setFocus(true);
3231 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3232 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3233 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3235 anotherInput.setFocus(false);
3236 QVERIFY(qApp->focusObject() != &anotherInput);
3237 QCOMPARE(view.activeFocusItem(), view.rootItem());
3238 anotherInput.setFocus(true);
3240 qApp->inputMethod()->hide();
3242 // input panel should not be opened if TextInput is read only
3243 input->setReadOnly(true);
3244 input->setFocus(true);
3245 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3246 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3247 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3248 QGuiApplication::processEvents();
3249 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3251 // input panel should not be opened if focusOnPress is set to false
3252 input->setFocusOnPress(false);
3253 input->setFocus(false);
3254 input->setFocus(true);
3255 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3256 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3257 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3258 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3261 class MyTextInput : public QQuickTextInput
3264 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3268 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3271 return QQuickTextInput::updatePaintNode(node, data);
3276 void tst_qquicktextinput::setHAlignClearCache()
3280 input.setText("Hello world");
3281 input.setParentItem(view.rootItem());
3283 view.requestActivateWindow();
3284 QTest::qWaitForWindowActive(&view);
3286 QEXPECT_FAIL("", "QTBUG-23485", Abort);
3288 QTRY_COMPARE(input.nbPaint, 1);
3289 input.setHAlign(QQuickTextInput::AlignRight);
3290 //Changing the alignment should trigger a repaint
3291 QTRY_COMPARE(input.nbPaint, 2);
3294 void tst_qquicktextinput::focusOutClearSelection()
3297 QQuickTextInput input;
3298 QQuickTextInput input2;
3299 input.setText(QLatin1String("Hello world"));
3300 input.setFocus(true);
3301 input2.setParentItem(view.rootItem());
3302 input.setParentItem(view.rootItem());
3303 input.componentComplete();
3304 input2.componentComplete();
3306 view.requestActivateWindow();
3307 QTest::qWaitForWindowActive(&view);
3308 QVERIFY(input.hasActiveFocus());
3310 //The selection should work
3311 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3312 input2.setFocus(true);
3313 QGuiApplication::processEvents();
3314 //The input lost the focus selection should be cleared
3315 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3318 void tst_qquicktextinput::geometrySignals()
3320 QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3321 QObject *o = component.create();
3323 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3324 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3328 void tst_qquicktextinput::contentSize()
3330 QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3331 QQmlComponent textComponent(&engine);
3332 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3333 QScopedPointer<QObject> object(textComponent.create());
3334 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3336 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3338 textObject->setText("The quick red fox jumped over the lazy brown dog");
3340 QVERIFY(textObject->contentWidth() > textObject->width());
3341 QVERIFY(textObject->contentHeight() < textObject->height());
3342 QCOMPARE(spy.count(), 1);
3344 textObject->setWrapMode(QQuickTextInput::WordWrap);
3345 QVERIFY(textObject->contentWidth() <= textObject->width());
3346 QVERIFY(textObject->contentHeight() > textObject->height());
3347 QCOMPARE(spy.count(), 2);
3349 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3351 QVERIFY(textObject->contentWidth() > textObject->width());
3352 QVERIFY(textObject->contentHeight() > textObject->height());
3353 QCOMPARE(spy.count(), 3);
3355 textObject->setText("The quick red fox jumped over the lazy brown dog");
3356 for (int w = 60; w < 120; ++w) {
3357 textObject->setWidth(w);
3358 QVERIFY(textObject->contentWidth() <= textObject->width());
3359 QVERIFY(textObject->contentHeight() > textObject->height());
3363 static void sendPreeditText(QQuickItem *item, const QString &text, int cursor)
3365 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3366 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3367 QCoreApplication::sendEvent(item, &event);
3370 void tst_qquicktextinput::preeditAutoScroll()
3372 QString preeditText = "califragisiticexpialidocious!";
3374 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3376 view.requestActivateWindow();
3377 QTest::qWaitForWindowActive(&view);
3378 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3380 QVERIFY(input->hasActiveFocus());
3382 input->setWidth(input->implicitWidth());
3384 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3385 int cursorRectangleChanges = 0;
3387 // test the text is scrolled so the preedit is visible.
3388 sendPreeditText(input, preeditText.mid(0, 3), 1);
3389 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3390 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3391 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3393 // test the text is scrolled back when the preedit is removed.
3394 QInputMethodEvent imEvent;
3395 QCoreApplication::sendEvent(input, &imEvent);
3396 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3397 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3398 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3400 QTextLayout layout(preeditText);
3401 layout.setFont(input->font());
3402 if (!qmlDisableDistanceField()) {
3404 option.setUseDesignMetrics(true);
3405 layout.setTextOption(option);
3407 layout.beginLayout();
3408 QTextLine line = layout.createLine();
3411 // test if the preedit is larger than the text input that the
3412 // character preceding the cursor is still visible.
3413 qreal x = input->positionToRectangle(0).x();
3414 for (int i = 0; i < 3; ++i) {
3415 sendPreeditText(input, preeditText, i + 1);
3416 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3417 QVERIFY(input->cursorRectangle().right() >= width - 3);
3418 QVERIFY(input->positionToRectangle(0).x() < x);
3419 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3420 x = input->positionToRectangle(0).x();
3422 for (int i = 1; i >= 0; --i) {
3423 sendPreeditText(input, preeditText, i + 1);
3424 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3425 QVERIFY(input->cursorRectangle().right() >= width - 3);
3426 QVERIFY(input->positionToRectangle(0).x() > x);
3427 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3428 x = input->positionToRectangle(0).x();
3431 // Test incrementing the preedit cursor doesn't cause further
3432 // scrolling when right most text is visible.
3433 sendPreeditText(input, preeditText, preeditText.length() - 3);
3434 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3435 x = input->positionToRectangle(0).x();
3436 for (int i = 2; i >= 0; --i) {
3437 sendPreeditText(input, preeditText, preeditText.length() - i);
3438 QCOMPARE(input->positionToRectangle(0).x(), x);
3439 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3441 for (int i = 1; i < 3; ++i) {
3442 sendPreeditText(input, preeditText, preeditText.length() - i);
3443 QCOMPARE(input->positionToRectangle(0).x(), x);
3444 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3447 // Test disabling auto scroll.
3448 QCoreApplication::sendEvent(input, &imEvent);
3450 input->setAutoScroll(false);
3451 sendPreeditText(input, preeditText.mid(0, 3), 1);
3452 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3453 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3456 void tst_qquicktextinput::preeditCursorRectangle()
3458 QString preeditText = "super";
3460 QQuickView view(testFileUrl("inputMethodEvent.qml"));
3462 view.requestActivateWindow();
3463 QTest::qWaitForWindowActive(&view);
3464 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3466 QVERIFY(input->hasActiveFocus());
3468 QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3473 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3474 QCoreApplication::sendEvent(input, &query);
3475 QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3477 // Verify that the micro focus rect is positioned the same for position 0 as
3478 // it would be if there was no preedit text.
3479 sendPreeditText(input, preeditText, 0);
3480 QCoreApplication::sendEvent(input, &query);
3481 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3482 QCOMPARE(currentRect, previousRect);
3483 QCOMPARE(input->cursorRectangle(), currentRect);
3484 QCOMPARE(cursor->pos(), currentRect.topLeft());
3486 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3487 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3489 // Verify that the micro focus rect moves to the left as the cursor position
3491 for (int i = 1; i <= 5; ++i) {
3492 sendPreeditText(input, preeditText, i);
3493 QCoreApplication::sendEvent(input, &query);
3494 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3495 QVERIFY(previousRect.left() < currentRect.left());
3496 QCOMPARE(input->cursorRectangle(), currentRect);
3497 QCOMPARE(cursor->pos(), currentRect.topLeft());
3498 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3499 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3500 previousRect = currentRect;
3503 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3504 // but the (non-zero) cursor position is the same.
3507 sendPreeditText(input, "wwwww", 5);
3508 QCoreApplication::sendEvent(input, &query);
3509 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3510 QCOMPARE(input->cursorRectangle(), currentRect);
3511 QCOMPARE(cursor->pos(), currentRect.topLeft());
3512 QCOMPARE(inputSpy.count(), 1);
3513 QCOMPARE(panelSpy.count(), 1);
3515 // Verify that if there is no preedit cursor then the micro focus rect is the
3516 // same as it would be if it were positioned at the end of the preedit text.
3519 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3520 QCoreApplication::sendEvent(input, &imEvent); }
3521 QCoreApplication::sendEvent(input, &query);
3522 currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3523 QCOMPARE(currentRect, previousRect);
3524 QCOMPARE(input->cursorRectangle(), currentRect);
3525 QCOMPARE(cursor->pos(), currentRect.topLeft());
3526 QCOMPARE(inputSpy.count(), 1);
3527 QCOMPARE(panelSpy.count(), 1);
3530 void tst_qquicktextinput::inputContextMouseHandler()
3532 PlatformInputContext platformInputContext;
3533 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3534 inputMethodPrivate->testContext = &platformInputContext;
3536 QString text = "supercalifragisiticexpialidocious!";
3537 QQuickView view(testFileUrl("inputContext.qml"));
3538 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3541 input->setFocus(true);
3545 view.requestActivateWindow();
3546 QTest::qWaitForWindowActive(&view);
3548 QTextLayout layout(text);
3549 layout.setFont(input->font());
3550 if (!qmlDisableDistanceField()) {
3552 option.setUseDesignMetrics(true);
3553 layout.setTextOption(option);
3555 layout.beginLayout();
3556 QTextLine line = layout.createLine();
3559 const qreal x = line.cursorToX(2, QTextLine::Leading);
3560 const qreal y = line.height() / 2;
3561 QPoint position = QPointF(x, y).toPoint();
3563 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3564 QGuiApplication::sendEvent(input, &inputEvent);
3566 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3567 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3568 QGuiApplication::processEvents();
3570 QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3571 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3572 QCOMPARE(platformInputContext.m_cursorPosition, 2);
3575 void tst_qquicktextinput::inputMethodComposing()
3577 QString text = "supercalifragisiticexpialidocious!";
3579 QQuickView view(testFileUrl("inputContext.qml"));
3581 view.requestActivateWindow();
3582 QTest::qWaitForWindowActive(&view);
3583 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3585 QVERIFY(input->hasActiveFocus());
3586 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3588 QCOMPARE(input->isInputMethodComposing(), false);
3590 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3591 QGuiApplication::sendEvent(input, &event);
3593 QCOMPARE(input->isInputMethodComposing(), true);
3594 QCOMPARE(spy.count(), 1);
3597 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3598 QGuiApplication::sendEvent(input, &event);
3600 QCOMPARE(spy.count(), 1);
3603 QInputMethodEvent event;
3604 QGuiApplication::sendEvent(input, &event);
3606 QCOMPARE(input->isInputMethodComposing(), false);
3607 QCOMPARE(spy.count(), 2);
3609 // Changing the text while not composing doesn't alter the composing state.
3610 input->setText(text.mid(0, 16));
3611 QCOMPARE(input->isInputMethodComposing(), false);
3612 QCOMPARE(spy.count(), 2);
3615 QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3616 QGuiApplication::sendEvent(input, &event);
3618 QCOMPARE(input->isInputMethodComposing(), true);
3619 QCOMPARE(spy.count(), 3);
3621 // Changing the text while composing cancels composition.
3622 input->setText(text.mid(0, 12));
3623 QCOMPARE(input->isInputMethodComposing(), false);
3624 QCOMPARE(spy.count(), 4);
3626 { // Preedit cursor positioned outside (empty) preedit; composing.
3627 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3628 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3629 QGuiApplication::sendEvent(input, &event);
3631 QCOMPARE(input->isInputMethodComposing(), true);
3632 QCOMPARE(spy.count(), 5);
3635 { // Cursor hidden; composing
3636 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3637 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3638 QGuiApplication::sendEvent(input, &event);
3640 QCOMPARE(input->isInputMethodComposing(), true);
3641 QCOMPARE(spy.count(), 5);
3643 { // Default cursor attributes; composing.
3644 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3645 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3646 QGuiApplication::sendEvent(input, &event);
3648 QCOMPARE(input->isInputMethodComposing(), true);
3649 QCOMPARE(spy.count(), 5);
3651 { // Selections are persisted: not composing
3652 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3653 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3654 QGuiApplication::sendEvent(input, &event);
3656 QCOMPARE(input->isInputMethodComposing(), false);
3657 QCOMPARE(spy.count(), 6);
3659 input->setCursorPosition(12);
3661 { // Formatting applied; composing.
3662 QTextCharFormat format;
3663 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3664 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3665 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3666 QGuiApplication::sendEvent(input, &event);
3668 QCOMPARE(input->isInputMethodComposing(), true);
3669 QCOMPARE(spy.count(), 7);
3672 QInputMethodEvent event;
3673 QGuiApplication::sendEvent(input, &event);
3675 QCOMPARE(input->isInputMethodComposing(), false);
3676 QCOMPARE(spy.count(), 8);
3679 void tst_qquicktextinput::inputMethodUpdate()
3681 PlatformInputContext platformInputContext;
3682 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3683 inputMethodPrivate->testContext = &platformInputContext;
3685 QQuickView view(testFileUrl("inputContext.qml"));
3687 view.requestActivateWindow();
3688 QTest::qWaitForWindowActive(&view);
3689 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3691 QVERIFY(input->hasActiveFocus());
3693 // text change even without cursor position change needs to trigger update
3694 input->setText("test");
3695 platformInputContext.clear();
3696 input->setText("xxxx");
3697 QVERIFY(platformInputContext.m_updateCallCount > 0);
3699 // input method event replacing text
3700 platformInputContext.clear();
3702 QInputMethodEvent inputMethodEvent;
3703 inputMethodEvent.setCommitString("y", -1, 1);
3704 QGuiApplication::sendEvent(input, &inputMethodEvent);
3706 QVERIFY(platformInputContext.m_updateCallCount > 0);
3708 // input method changing selection
3709 platformInputContext.clear();
3711 QList<QInputMethodEvent::Attribute> attributes;
3712 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3713 QInputMethodEvent inputMethodEvent("", attributes);
3714 QGuiApplication::sendEvent(input, &inputMethodEvent);
3716 QVERIFY(input->selectionStart() != input->selectionEnd());
3717 QVERIFY(platformInputContext.m_updateCallCount > 0);
3719 // programmatical selections trigger update
3720 platformInputContext.clear();
3722 QVERIFY(platformInputContext.m_updateCallCount > 0);
3725 platformInputContext.clear();
3726 QFont font = input->font();
3727 font.setBold(!font.bold());
3728 input->setFont(font);
3729 QVERIFY(platformInputContext.m_updateCallCount > 0);
3732 platformInputContext.clear();
3734 QInputMethodEvent inputMethodEvent;
3735 inputMethodEvent.setCommitString("y");
3736 QGuiApplication::sendEvent(input, &inputMethodEvent);
3738 QVERIFY(platformInputContext.m_updateCallCount > 0);
3740 // changing cursor position
3741 platformInputContext.clear();
3742 input->setCursorPosition(0);
3743 QVERIFY(platformInputContext.m_updateCallCount > 0);
3745 // read only disabled input method
3746 platformInputContext.clear();
3747 input->setReadOnly(true);
3748 QVERIFY(platformInputContext.m_updateCallCount > 0);
3749 input->setReadOnly(false);
3751 // no updates while no focus
3752 input->setFocus(false);
3753 platformInputContext.clear();
3754 input->setText("Foo");
3755 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3756 input->setCursorPosition(1);
3757 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3759 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3760 input->setReadOnly(true);
3761 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3764 void tst_qquicktextinput::cursorRectangleSize()
3766 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3767 QVERIFY(canvas->rootObject() != 0);
3768 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3770 // make sure cursor rectangle is not at (0,0)
3771 textInput->setX(10);
3772 textInput->setY(10);
3773 textInput->setCursorPosition(3);
3774 QVERIFY(textInput != 0);
3775 textInput->setFocus(true);
3777 canvas->requestActivateWindow();
3778 QTest::qWaitForWindowActive(canvas);
3779 QVERIFY(textInput->hasActiveFocus());
3781 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3782 qApp->sendEvent(textInput, &event);
3783 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3785 QRectF cursorRectFromItem = textInput->cursorRectangle();
3786 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3788 // item and input query cursor rectangles match
3789 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3791 // item cursor rectangle and positionToRectangle calculations match
3792 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3794 // item-canvas transform and input item transform match
3795 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3797 // input panel cursorRectangle property and tranformed item cursor rectangle match
3798 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3799 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3804 void tst_qquicktextinput::tripleClickSelectsAll()
3806 QString qmlfile = testFile("positionAt.qml");
3807 QQuickView view(QUrl::fromLocalFile(qmlfile));
3809 view.requestActivateWindow();
3810 QTest::qWaitForWindowActive(&view);
3812 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3815 QLatin1String hello("Hello world!");
3816 input->setSelectByMouse(true);
3817 input->setText(hello);
3819 // Clicking on the same point inside TextInput three times in a row
3820 // should trigger a triple click, thus selecting all the text.
3821 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3822 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3823 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3824 QGuiApplication::processEvents();
3825 QCOMPARE(input->selectedText(), hello);
3827 // Now it simulates user moving the mouse between the second and the third click.
3828 // In this situation, we don't expect a triple click.
3829 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3830 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3831 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3832 QGuiApplication::processEvents();
3833 QVERIFY(input->selectedText().isEmpty());
3835 // And now we press the third click too late, so no triple click event is triggered.
3836 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3837 QGuiApplication::processEvents();
3838 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3839 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3840 QGuiApplication::processEvents();
3841 QVERIFY(input->selectedText().isEmpty());
3844 void tst_qquicktextinput::QTBUG_19956_data()
3846 QTest::addColumn<QString>("url");
3847 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3848 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3852 void tst_qquicktextinput::getText_data()
3854 QTest::addColumn<QString>("text");
3855 QTest::addColumn<QString>("inputMask");
3856 QTest::addColumn<int>("start");
3857 QTest::addColumn<int>("end");
3858 QTest::addColumn<QString>("expectedText");
3860 QTest::newRow("all plain text")
3863 << 0 << standard.at(0).length()
3866 QTest::newRow("plain text sub string")
3870 << standard.at(0).mid(0, 12);
3872 QTest::newRow("plain text sub string reversed")
3876 << standard.at(0).mid(0, 12);
3878 QTest::newRow("plain text cropped beginning")
3882 << standard.at(0).mid(0, 4);
3884 QTest::newRow("plain text cropped end")
3887 << 23 << standard.at(0).length() + 8
3888 << standard.at(0).mid(23);
3890 QTest::newRow("plain text cropped beginning and end")
3893 << -9 << standard.at(0).length() + 4
3897 void tst_qquicktextinput::getText()
3899 QFETCH(QString, text);
3900 QFETCH(QString, inputMask);
3903 QFETCH(QString, expectedText);
3905 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3906 QQmlComponent textInputComponent(&engine);
3907 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3908 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3909 QVERIFY(textInput != 0);
3911 QCOMPARE(textInput->getText(start, end), expectedText);
3914 void tst_qquicktextinput::insert_data()
3916 QTest::addColumn<QString>("text");
3917 QTest::addColumn<QString>("inputMask");
3918 QTest::addColumn<int>("selectionStart");
3919 QTest::addColumn<int>("selectionEnd");
3920 QTest::addColumn<int>("insertPosition");
3921 QTest::addColumn<QString>("insertText");
3922 QTest::addColumn<QString>("expectedText");
3923 QTest::addColumn<int>("expectedSelectionStart");
3924 QTest::addColumn<int>("expectedSelectionEnd");
3925 QTest::addColumn<int>("expectedCursorPosition");
3926 QTest::addColumn<bool>("selectionChanged");
3927 QTest::addColumn<bool>("cursorPositionChanged");
3929 QTest::newRow("at cursor position (beginning)")
3934 << QString("Hello") + standard.at(0)
3938 QTest::newRow("at cursor position (end)")
3941 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3943 << standard.at(0) + QString("Hello")
3944 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3947 QTest::newRow("at cursor position (middle)")
3952 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3956 QTest::newRow("after cursor position (beginning)")
3961 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3965 QTest::newRow("before cursor position (end)")
3968 << standard.at(0).length() << standard.at(0).length() << 18
3970 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3971 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3974 QTest::newRow("before cursor position (middle)")
3979 << QString("Hello") + standard.at(0)
3983 QTest::newRow("after cursor position (middle)")
3986 << 18 << 18 << standard.at(0).length()
3988 << standard.at(0) + QString("Hello")
3992 QTest::newRow("before selection")
3997 << QString("Hello") + standard.at(0)
4001 QTest::newRow("before reversed selection")
4006 << QString("Hello") + standard.at(0)
4010 QTest::newRow("after selection")
4013 << 14 << 19 << standard.at(0).length()
4015 << standard.at(0) + QString("Hello")
4019 QTest::newRow("after reversed selection")
4022 << 19 << 14 << standard.at(0).length()
4024 << standard.at(0) + QString("Hello")
4028 QTest::newRow("into selection")
4033 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4037 QTest::newRow("into reversed selection")
4042 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4046 QTest::newRow("rich text into plain text")
4050 << QString("<b>Hello</b>")
4051 << QString("<b>Hello</b>") + standard.at(0)
4055 QTest::newRow("before start")
4064 QTest::newRow("past end")
4067 << 0 << 0 << standard.at(0).length() + 3
4073 const QString inputMask = "009.009.009.009";
4074 const QString ip = "192.168.2.14";
4076 QTest::newRow("mask: at cursor position (beginning)")
4081 << QString("125.168.2.14")
4085 QTest::newRow("mask: at cursor position (end)")
4088 << inputMask.length() << inputMask.length() << inputMask.length()
4091 << inputMask.length() << inputMask.length() << inputMask.length()
4094 QTest::newRow("mask: at cursor position (middle)")
4099 << QString("192.167.5.24")
4103 QTest::newRow("mask: after cursor position (beginning)")
4108 << QString("192.167.5.24")
4112 QTest::newRow("mask: before cursor position (end)")
4115 << inputMask.length() << inputMask.length() << 6
4117 << QString("192.167.5.24")
4118 << inputMask.length() << inputMask.length() << inputMask.length()
4121 QTest::newRow("mask: before cursor position (middle)")
4126 << QString("125.168.2.14")
4130 QTest::newRow("mask: after cursor position (middle)")
4139 QTest::newRow("mask: before selection")
4144 << QString("125.168.2.14")
4148 QTest::newRow("mask: before reversed selection")
4153 << QString("125.168.2.14")
4157 QTest::newRow("mask: after selection")
4166 QTest::newRow("mask: after reversed selection")
4175 QTest::newRow("mask: into selection")
4180 << QString("192.167.5.24")
4184 QTest::newRow("mask: into reversed selection")
4189 << QString("192.167.5.24")
4193 QTest::newRow("mask: before start")
4202 QTest::newRow("mask: past end")
4205 << 0 << 0 << ip.length() + 3
4211 QTest::newRow("mask: invalid characters")
4216 << QString("192.168.2.14")
4220 QTest::newRow("mask: mixed validity")
4224 << QString("a1b2c5")
4225 << QString("125.168.2.14")
4230 void tst_qquicktextinput::insert()
4232 QFETCH(QString, text);
4233 QFETCH(QString, inputMask);
4234 QFETCH(int, selectionStart);
4235 QFETCH(int, selectionEnd);
4236 QFETCH(int, insertPosition);
4237 QFETCH(QString, insertText);
4238 QFETCH(QString, expectedText);
4239 QFETCH(int, expectedSelectionStart);
4240 QFETCH(int, expectedSelectionEnd);
4241 QFETCH(int, expectedCursorPosition);
4242 QFETCH(bool, selectionChanged);
4243 QFETCH(bool, cursorPositionChanged);
4245 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4246 QQmlComponent textInputComponent(&engine);
4247 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4248 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4249 QVERIFY(textInput != 0);
4251 textInput->select(selectionStart, selectionEnd);
4253 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4254 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4255 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4256 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4257 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4259 textInput->insert(insertPosition, insertText);
4261 QCOMPARE(textInput->text(), expectedText);
4262 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4264 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4265 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4266 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4268 if (selectionStart > selectionEnd)
4269 qSwap(selectionStart, selectionEnd);
4271 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4272 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4273 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4274 QCOMPARE(textSpy.count() > 0, text != expectedText);
4275 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4278 void tst_qquicktextinput::remove_data()
4280 QTest::addColumn<QString>("text");
4281 QTest::addColumn<QString>("inputMask");
4282 QTest::addColumn<int>("selectionStart");
4283 QTest::addColumn<int>("selectionEnd");
4284 QTest::addColumn<int>("removeStart");
4285 QTest::addColumn<int>("removeEnd");
4286 QTest::addColumn<QString>("expectedText");
4287 QTest::addColumn<int>("expectedSelectionStart");
4288 QTest::addColumn<int>("expectedSelectionEnd");
4289 QTest::addColumn<int>("expectedCursorPosition");
4290 QTest::addColumn<bool>("selectionChanged");
4291 QTest::addColumn<bool>("cursorPositionChanged");
4293 QTest::newRow("from cursor position (beginning)")
4298 << standard.at(0).mid(5)
4302 QTest::newRow("to cursor position (beginning)")
4307 << standard.at(0).mid(5)
4311 QTest::newRow("to cursor position (end)")
4314 << standard.at(0).length() << standard.at(0).length()
4315 << standard.at(0).length() << standard.at(0).length() - 5
4316 << standard.at(0).mid(0, standard.at(0).length() - 5)
4317 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4320 QTest::newRow("to cursor position (end)")
4323 << standard.at(0).length() << standard.at(0).length()
4324 << standard.at(0).length() - 5 << standard.at(0).length()
4325 << standard.at(0).mid(0, standard.at(0).length() - 5)
4326 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4329 QTest::newRow("from cursor position (middle)")
4334 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4338 QTest::newRow("to cursor position (middle)")
4343 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4347 QTest::newRow("after cursor position (beginning)")
4352 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4356 QTest::newRow("before cursor position (end)")
4359 << standard.at(0).length() << standard.at(0).length()
4361 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4362 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4365 QTest::newRow("before cursor position (middle)")
4370 << standard.at(0).mid(5)
4374 QTest::newRow("after cursor position (middle)")
4379 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4383 QTest::newRow("before selection")
4388 << standard.at(0).mid(5)
4392 QTest::newRow("before reversed selection")
4397 << standard.at(0).mid(5)
4401 QTest::newRow("after selection")
4405 << standard.at(0).length() - 5 << standard.at(0).length()
4406 << standard.at(0).mid(0, standard.at(0).length() - 5)
4410 QTest::newRow("after reversed selection")
4414 << standard.at(0).length() - 5 << standard.at(0).length()
4415 << standard.at(0).mid(0, standard.at(0).length() - 5)
4419 QTest::newRow("from selection")
4424 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4428 QTest::newRow("from reversed selection")
4433 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4437 QTest::newRow("cropped beginning")
4442 << standard.at(0).mid(4)
4446 QTest::newRow("cropped end")
4450 << 23 << standard.at(0).length() + 8
4451 << standard.at(0).mid(0, 23)
4455 QTest::newRow("cropped beginning and end")
4459 << -9 << standard.at(0).length() + 4
4464 const QString inputMask = "009.009.009.009";
4465 const QString ip = "192.168.2.14";
4467 QTest::newRow("mask: from cursor position")
4472 << QString("192.16..14")
4476 QTest::newRow("mask: to cursor position")
4481 << QString("19.8.2.14")
4485 QTest::newRow("mask: before cursor position")
4490 << QString("2.168.2.14")
4494 QTest::newRow("mask: after cursor position")
4499 << QString("192.168.2.")
4503 QTest::newRow("mask: before selection")
4508 << QString("2.168.2.14")
4512 QTest::newRow("mask: before reversed selection")
4517 << QString("2.168.2.14")
4521 QTest::newRow("mask: after selection")
4526 << QString("192.168.2.")
4530 QTest::newRow("mask: after reversed selection")
4535 << QString("192.168.2.")
4539 QTest::newRow("mask: from selection")
4544 << QString("192.168..14")
4548 QTest::newRow("mask: from reversed selection")
4553 << QString("192.168..14")
4557 QTest::newRow("mask: cropped beginning")
4562 << QString(".168.2.14")
4566 QTest::newRow("mask: cropped end")
4571 << QString("192.168.2.1")
4575 QTest::newRow("mask: cropped beginning and end")
4585 void tst_qquicktextinput::remove()
4587 QFETCH(QString, text);
4588 QFETCH(QString, inputMask);
4589 QFETCH(int, selectionStart);
4590 QFETCH(int, selectionEnd);
4591 QFETCH(int, removeStart);
4592 QFETCH(int, removeEnd);
4593 QFETCH(QString, expectedText);
4594 QFETCH(int, expectedSelectionStart);
4595 QFETCH(int, expectedSelectionEnd);
4596 QFETCH(int, expectedCursorPosition);
4597 QFETCH(bool, selectionChanged);
4598 QFETCH(bool, cursorPositionChanged);
4600 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4601 QQmlComponent textInputComponent(&engine);
4602 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4603 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4604 QVERIFY(textInput != 0);
4606 textInput->select(selectionStart, selectionEnd);
4608 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4609 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4610 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4611 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4612 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4614 textInput->remove(removeStart, removeEnd);
4616 QCOMPARE(textInput->text(), expectedText);
4617 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4619 if (selectionStart > selectionEnd) //
4620 qSwap(selectionStart, selectionEnd);
4622 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4623 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4624 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4626 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4627 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4628 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4629 QCOMPARE(textSpy.count() > 0, text != expectedText);
4631 if (cursorPositionChanged) //
4632 QVERIFY(cursorPositionSpy.count() > 0);
4635 void tst_qquicktextinput::keySequence_data()
4637 QTest::addColumn<QString>("text");
4638 QTest::addColumn<QKeySequence>("sequence");
4639 QTest::addColumn<int>("selectionStart");
4640 QTest::addColumn<int>("selectionEnd");
4641 QTest::addColumn<int>("cursorPosition");
4642 QTest::addColumn<QString>("expectedText");
4643 QTest::addColumn<QString>("selectedText");
4644 QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4645 QTest::addColumn<Qt::Key>("layoutDirection");
4647 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4649 QTest::newRow("select all")
4650 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4651 << 44 << standard.at(0) << standard.at(0)
4652 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4653 QTest::newRow("select start of line")
4654 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4655 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4656 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4657 QTest::newRow("select start of block")
4658 << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4659 << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4660 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4661 QTest::newRow("select end of line")
4662 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4663 << 44 << standard.at(0) << standard.at(0).mid(5)
4664 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4665 QTest::newRow("select end of document") // ### Not handled.
4666 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4667 << 3 << standard.at(0) << QString()
4668 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4669 QTest::newRow("select end of block")
4670 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4671 << 44 << standard.at(0) << standard.at(0).mid(18)
4672 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4673 QTest::newRow("delete end of line")
4674 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4675 << 24 << standard.at(0).mid(0, 24) << QString()
4676 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4677 QTest::newRow("move to start of line")
4678 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4679 << 0 << standard.at(0) << QString()
4680 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4681 QTest::newRow("move to start of block")
4682 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4683 << 0 << standard.at(0) << QString()
4684 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4685 QTest::newRow("move to next char")
4686 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4687 << 13 << standard.at(0) << QString()
4688 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4689 QTest::newRow("move to previous char (ltr)")
4690 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4691 << 2 << standard.at(0) << QString()
4692 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4693 QTest::newRow("move to previous char (rtl)")
4694 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4695 << 4 << standard.at(0) << QString()
4696 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4697 QTest::newRow("move to previous char with selection")
4698 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4699 << 3 << standard.at(0) << QString()
4700 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4701 QTest::newRow("select next char (ltr)")
4702 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4703 << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4704 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4705 QTest::newRow("select next char (rtl)")
4706 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4707 << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4708 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4709 QTest::newRow("select previous char (ltr)")
4710 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4711 << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4712 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4713 QTest::newRow("select previous char (rtl)")
4714 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4715 << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4716 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4717 QTest::newRow("move to next word (ltr)")
4718 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4719 << 10 << standard.at(0) << QString()
4720 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4721 QTest::newRow("move to next word (rtl)")
4722 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4723 << 4 << standard.at(0) << QString()
4724 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4725 QTest::newRow("move to next word (password,ltr)")
4726 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4727 << 44 << standard.at(0) << QString()
4728 << QQuickTextInput::Password << Qt::Key_Direction_L;
4729 QTest::newRow("move to next word (password,rtl)")
4730 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4731 << 0 << standard.at(0) << QString()
4732 << QQuickTextInput::Password << Qt::Key_Direction_R;
4733 QTest::newRow("move to previous word (ltr)")
4734 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4735 << 4 << standard.at(0) << QString()
4736 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4737 QTest::newRow("move to previous word (rlt)")
4738 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4739 << 10 << standard.at(0) << QString()
4740 << QQuickTextInput::Normal << Qt::Key_Direction_R;
4741 QTest::newRow("move to previous word (password,ltr)")
4742 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4743 << 0 << standard.at(0) << QString()
4744 << QQuickTextInput::Password << Qt::Key_Direction_L;
4745 QTest::newRow("move to previous word (password,rtl)")
4746 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4747 << 44 << standard.at(0) << QString()
4748 << QQuickTextInput::Password << Qt::Key_Direction_R;
4749 QTest::newRow("select next word")
4750 << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4751 << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4752 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4753 QTest::newRow("select previous word")
4754 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4755 << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4756 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4757 QTest::newRow("delete (selection)")
4758 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4759 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4760 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4761 QTest::newRow("delete (no selection)")
4762 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4763 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4764 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4765 QTest::newRow("delete end of word")
4766 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4767 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4768 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4769 QTest::newRow("delete start of word")
4770 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4771 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4772 << QQuickTextInput::Normal << Qt::Key_Direction_L;
4775 void tst_qquicktextinput::keySequence()
4777 QFETCH(QString, text);
4778 QFETCH(QKeySequence, sequence);
4779 QFETCH(int, selectionStart);
4780 QFETCH(int, selectionEnd);
4781 QFETCH(int, cursorPosition);
4782 QFETCH(QString, expectedText);
4783 QFETCH(QString, selectedText);
4784 QFETCH(QQuickTextInput::EchoMode, echoMode);
4785 QFETCH(Qt::Key, layoutDirection);
4787 if (sequence.isEmpty()) {
4788 QSKIP("Key sequence is undefined");
4791 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4792 QQmlComponent textInputComponent(&engine);
4793 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4794 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4795 QVERIFY(textInput != 0);
4796 textInput->setEchoMode(echoMode);
4798 QQuickCanvas canvas;
4799 textInput->setParentItem(canvas.rootItem());
4801 canvas.requestActivateWindow();
4802 QTest::qWaitForWindowActive(&canvas);
4803 QVERIFY(textInput->hasActiveFocus());
4805 simulateKey(&canvas, layoutDirection);
4807 textInput->select(selectionStart, selectionEnd);
4809 simulateKeys(&canvas, sequence);
4811 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4812 QCOMPARE(textInput->text(), expectedText);
4813 QCOMPARE(textInput->selectedText(), selectedText);
4817 #define REPLACE_UNTIL_END 1
4819 void tst_qquicktextinput::undo_data()
4821 QTest::addColumn<QStringList>("insertString");
4822 QTest::addColumn<IntList>("insertIndex");
4823 QTest::addColumn<IntList>("insertMode");
4824 QTest::addColumn<QStringList>("expectedString");
4825 QTest::addColumn<bool>("use_keys");
4827 for (int i=0; i<2; i++) {
4828 QString keys_str = "keyboard";
4829 bool use_keys = true;
4831 keys_str = "insert";
4836 IntList insertIndex;
4838 QStringList insertString;
4839 QStringList expectedString;
4842 insertMode << NORMAL;
4843 insertString << "1";
4846 insertMode << NORMAL;
4847 insertString << "5";
4850 insertMode << NORMAL;
4851 insertString << "3";
4854 insertMode << NORMAL;
4855 insertString << "2";
4858 insertMode << NORMAL;
4859 insertString << "4";
4861 expectedString << "12345";
4862 expectedString << "1235";
4863 expectedString << "135";
4864 expectedString << "15";
4865 expectedString << "";
4867 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4875 IntList insertIndex;
4877 QStringList insertString;
4878 QStringList expectedString;
4881 insertMode << NORMAL;
4882 insertString << "World"; // World
4885 insertMode << NORMAL;
4886 insertString << "Hello"; // HelloWorld
4889 insertMode << NORMAL;
4890 insertString << "Well"; // WellHelloWorld
4893 insertMode << NORMAL;
4894 insertString << "There"; // WellHelloThereWorld;
4896 expectedString << "WellHelloThereWorld";
4897 expectedString << "WellHelloWorld";
4898 expectedString << "HelloWorld";
4899 expectedString << "World";
4900 expectedString << "";
4902 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4910 IntList insertIndex;
4912 QStringList insertString;
4913 QStringList expectedString;
4916 insertMode << NORMAL;
4917 insertString << "Ensuring";
4920 insertMode << NORMAL;
4921 insertString << " instan";
4924 insertMode << NORMAL;
4925 insertString << "an ";
4928 insertMode << REPLACE_UNTIL_END;
4929 insertString << " unique instance.";
4931 expectedString << "Ensuring a unique instance.";
4932 expectedString << "Ensuring an instan";
4933 expectedString << "Ensuring instan";
4934 expectedString << "";
4936 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4946 void tst_qquicktextinput::undo()
4948 QFETCH(QStringList, insertString);
4949 QFETCH(IntList, insertIndex);
4950 QFETCH(IntList, insertMode);
4951 QFETCH(QStringList, expectedString);
4953 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4954 QQmlComponent textInputComponent(&engine);
4955 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4956 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4957 QVERIFY(textInput != 0);
4959 QQuickCanvas canvas;
4960 textInput->setParentItem(canvas.rootItem());
4962 canvas.requestActivateWindow();
4963 QTest::qWaitForWindowActive(&canvas);
4964 QVERIFY(textInput->hasActiveFocus());
4966 QVERIFY(!textInput->canUndo());
4968 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4972 // STEP 1: First build up an undo history by inserting or typing some strings...
4973 for (i = 0; i < insertString.size(); ++i) {
4974 if (insertIndex[i] > -1)
4975 textInput->setCursorPosition(insertIndex[i]);
4977 // experimental stuff
4978 if (insertMode[i] == REPLACE_UNTIL_END) {
4979 textInput->select(insertIndex[i], insertIndex[i] + 8);
4981 // This is what I actually want...
4982 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4985 for (int j = 0; j < insertString.at(i).length(); j++)
4986 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4989 QCOMPARE(spy.count(), 1);
4991 // STEP 2: Next call undo several times and see if we can restore to the previous state
4992 for (i = 0; i < expectedString.size() - 1; ++i) {
4993 QCOMPARE(textInput->text(), expectedString[i]);
4994 QVERIFY(textInput->canUndo());
4998 // STEP 3: Verify that we have undone everything
4999 QVERIFY(textInput->text().isEmpty());
5000 QVERIFY(!textInput->canUndo());
5001 QCOMPARE(spy.count(), 2);
5004 void tst_qquicktextinput::redo_data()
5006 QTest::addColumn<QStringList>("insertString");
5007 QTest::addColumn<IntList>("insertIndex");
5008 QTest::addColumn<QStringList>("expectedString");
5011 IntList insertIndex;
5012 QStringList insertString;
5013 QStringList expectedString;
5016 insertString << "World"; // World
5018 insertString << "Hello"; // HelloWorld
5020 insertString << "Well"; // WellHelloWorld
5022 insertString << "There"; // WellHelloThereWorld;
5024 expectedString << "World";
5025 expectedString << "HelloWorld";
5026 expectedString << "WellHelloWorld";
5027 expectedString << "WellHelloThereWorld";
5029 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
5033 void tst_qquicktextinput::redo()
5035 QFETCH(QStringList, insertString);
5036 QFETCH(IntList, insertIndex);
5037 QFETCH(QStringList, expectedString);
5039 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5040 QQmlComponent textInputComponent(&engine);
5041 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5042 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5043 QVERIFY(textInput != 0);
5045 QQuickCanvas canvas;
5046 textInput->setParentItem(canvas.rootItem());
5048 canvas.requestActivateWindow();
5049 QTest::qWaitForWindowActive(&canvas);
5051 QVERIFY(textInput->hasActiveFocus());
5052 QVERIFY(!textInput->canUndo());
5053 QVERIFY(!textInput->canRedo());
5055 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5058 // inserts the diff strings at diff positions
5059 for (i = 0; i < insertString.size(); ++i) {
5060 if (insertIndex[i] > -1)
5061 textInput->setCursorPosition(insertIndex[i]);
5062 for (int j = 0; j < insertString.at(i).length(); j++)
5063 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
5064 QVERIFY(textInput->canUndo());
5065 QVERIFY(!textInput->canRedo());
5068 QCOMPARE(spy.count(), 0);
5071 while (!textInput->text().isEmpty()) {
5072 QVERIFY(textInput->canUndo());
5074 QVERIFY(textInput->canRedo());
5077 QCOMPARE(spy.count(), 1);
5079 for (i = 0; i < expectedString.size(); ++i) {
5080 QVERIFY(textInput->canRedo());
5082 QCOMPARE(textInput->text() , expectedString[i]);
5083 QVERIFY(textInput->canUndo());
5085 QVERIFY(!textInput->canRedo());
5086 QCOMPARE(spy.count(), 2);
5089 void tst_qquicktextinput::undo_keypressevents_data()
5091 QTest::addColumn<KeyList>("keys");
5092 QTest::addColumn<QStringList>("expectedString");
5096 QStringList expectedString;
5099 << QKeySequence::MoveToStartOfLine
5106 << QKeySequence::MoveToEndOfLine
5109 expectedString << "BEVERYAFRAID!";
5110 expectedString << "BEVERYAFRAID";
5111 expectedString << "VERYAFRAID";
5112 expectedString << "AFRAID";
5114 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5117 QStringList expectedString;
5120 keys << "1234" << QKeySequence::MoveToStartOfLine
5122 << Qt::Key_Right << Qt::Key_Right
5124 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5128 expectedString << "12";
5129 expectedString << "1234";
5131 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5134 QStringList expectedString;
5138 << QKeySequence::MoveToStartOfLine
5140 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5142 << QKeySequence::Undo
5144 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5147 expectedString << "AB";
5148 expectedString << "AB12";
5150 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5153 QStringList expectedString;
5158 << Qt::Key_Left << Qt::Key_Left
5162 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5163 // overwriting '1234' with '5'
5165 // undoing deletion of 'AB'
5166 << QKeySequence::Undo
5167 // overwriting '1234' with '6'
5170 expectedString << "ab6cd";
5171 // for versions previous to 3.2 we overwrite needed two undo operations
5172 expectedString << "ab1234cd";
5173 expectedString << "abcd";
5175 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5178 QStringList expectedString;
5183 << Qt::Key_Backspace;
5185 expectedString << "AB";
5186 expectedString << "ABC";
5188 QTest::newRow("Inserts,backspace") << keys << expectedString;
5191 QStringList expectedString;
5195 << Qt::Key_Backspace
5199 expectedString << "ABZ";
5200 expectedString << "AB";
5201 expectedString << "ABC";
5203 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5206 QStringList expectedString;
5209 keys << "123" << QKeySequence::MoveToStartOfLine
5211 << QKeySequence::SelectEndOfLine
5212 // overwriting '123' with 'ABC'
5215 expectedString << "ABC";
5216 // for versions previous to 3.2 we overwrite needed two undo operations
5217 expectedString << "123";
5219 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5222 QStringList expectedString;
5226 << QKeySequence::Undo
5227 << QKeySequence::Redo;
5229 expectedString << "123";
5230 expectedString << QString();
5232 QTest::newRow("Insert,undo,redo") << keys << expectedString;
5236 void tst_qquicktextinput::undo_keypressevents()
5238 QFETCH(KeyList, keys);
5239 QFETCH(QStringList, expectedString);
5241 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5242 QQmlComponent textInputComponent(&engine);
5243 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5244 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5245 QVERIFY(textInput != 0);
5247 QQuickCanvas canvas;
5248 textInput->setParentItem(canvas.rootItem());
5250 canvas.requestActivateWindow();
5251 QTest::qWaitForWindowActive(&canvas);
5252 QVERIFY(textInput->hasActiveFocus());
5254 simulateKeys(&canvas, keys);
5256 for (int i = 0; i < expectedString.size(); ++i) {
5257 QCOMPARE(textInput->text() , expectedString[i]);
5260 QVERIFY(textInput->text().isEmpty());
5263 void tst_qquicktextinput::QTBUG_19956()
5265 QFETCH(QString, url);
5267 QQuickView canvas(testFileUrl(url));
5269 canvas.requestActivateWindow();
5270 QTest::qWaitForWindowActive(&canvas);
5271 QVERIFY(canvas.rootObject() != 0);
5272 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5274 input->setFocus(true);
5275 QVERIFY(input->hasActiveFocus());
5277 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
5278 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5279 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
5280 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5282 canvas.rootObject()->setProperty("topvalue", 15);
5283 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
5284 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5286 canvas.rootObject()->setProperty("topvalue", 25);
5287 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
5288 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5290 canvas.rootObject()->setProperty("bottomvalue", 21);
5291 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
5292 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5294 canvas.rootObject()->setProperty("bottomvalue", 10);
5295 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5296 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5299 void tst_qquicktextinput::QTBUG_19956_regexp()
5301 QUrl url = testFileUrl("qtbug-19956regexp.qml");
5303 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5304 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5306 QQuickView canvas(url);
5308 canvas.requestActivateWindow();
5309 QTest::qWaitForWindowActive(&canvas);
5310 QVERIFY(canvas.rootObject() != 0);
5311 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5313 input->setFocus(true);
5314 QVERIFY(input->hasActiveFocus());
5316 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5317 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5318 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
5319 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5321 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5322 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5323 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5325 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5326 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5327 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5330 void tst_qquicktextinput::implicitSize_data()
5332 QTest::addColumn<QString>("text");
5333 QTest::addColumn<QString>("wrap");
5334 QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5335 QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5338 void tst_qquicktextinput::implicitSize()
5340 QFETCH(QString, text);
5341 QFETCH(QString, wrap);
5342 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5343 QQmlComponent textComponent(&engine);
5344 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5345 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5347 QVERIFY(textObject->width() < textObject->implicitWidth());
5348 QVERIFY(textObject->height() == textObject->implicitHeight());
5350 textObject->resetWidth();
5351 QVERIFY(textObject->width() == textObject->implicitWidth());
5352 QVERIFY(textObject->height() == textObject->implicitHeight());
5355 void tst_qquicktextinput::implicitSizeBinding_data()
5357 implicitSize_data();
5360 void tst_qquicktextinput::implicitSizeBinding()
5362 QFETCH(QString, text);
5363 QFETCH(QString, wrap);
5364 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5365 QQmlComponent textComponent(&engine);
5366 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5367 QScopedPointer<QObject> object(textComponent.create());
5368 QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5370 QCOMPARE(textObject->width(), textObject->implicitWidth());
5371 QCOMPARE(textObject->height(), textObject->implicitHeight());
5373 textObject->resetWidth();
5374 QCOMPARE(textObject->width(), textObject->implicitWidth());
5375 QCOMPARE(textObject->height(), textObject->implicitHeight());
5377 textObject->resetHeight();
5378 QCOMPARE(textObject->width(), textObject->implicitWidth());
5379 QCOMPARE(textObject->height(), textObject->implicitHeight());
5383 void tst_qquicktextinput::negativeDimensions()
5385 // Verify this doesn't assert during initialization.
5386 QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5387 QScopedPointer<QObject> o(component.create());
5389 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5391 QCOMPARE(input->width(), qreal(-1));
5392 QCOMPARE(input->height(), qreal(-1));
5396 void tst_qquicktextinput::setInputMask_data()
5398 QTest::addColumn<QString>("mask");
5399 QTest::addColumn<QString>("input");
5400 QTest::addColumn<QString>("expectedText");
5401 QTest::addColumn<QString>("expectedDisplay");
5402 QTest::addColumn<bool>("insert_text");
5404 // both keyboard and insert()
5405 for (int i=0; i<2; i++) {
5406 bool insert_text = i==0 ? false : true;
5407 QString insert_mode = "keys ";
5409 insert_mode = "insert ";
5411 QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5412 << QString("000.000.000.000")
5413 << QString("127.0.0.1")
5414 << QString("127.0.0.1")
5415 << QString("127.0 .0 .1 ")
5416 << bool(insert_text);
5417 QTest::newRow(QString(insert_mode + "mac").toLatin1())
5418 << QString("HH:HH:HH:HH:HH:HH;#")
5419 << QString("00:E0:81:21:9E:8E")
5420 << QString("00:E0:81:21:9E:8E")
5421 << QString("00:E0:81:21:9E:8E")
5422 << bool(insert_text);
5423 QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5424 << QString("<HH:>HH:!HH:HH:HH:HH;#")
5425 << QString("AAe081219E8E")
5426 << QString("aa:E0:81:21:9E:8E")
5427 << QString("aa:E0:81:21:9E:8E")
5428 << bool(insert_text);
5429 QTest::newRow(QString(insert_mode + "byte").toLatin1())
5430 << QString("BBBBBBBB;0")
5431 << QString("11011001")
5433 << QString("11011001")
5434 << bool(insert_text);
5435 QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5436 << QString("bbbb.bbbb;-")
5437 << QString("110. 0001")
5438 << QString("110.0001")
5439 << QString("110-.0001")
5440 << bool(insert_text);
5441 QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5442 << QString("000.000.000.000;0")
5443 << QString("127.0.0.1")
5444 << QString("127...1")
5445 << QString("127.000.000.100")
5446 << bool(insert_text);
5447 QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5448 << QString("000.000.000.000")
5449 << QString(".0.0.1")
5450 << QString(".0.0.1")
5451 << QString(" .0 .0 .1 ")
5452 << bool(insert_text);
5453 QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5454 << QString("000.000.000.000")
5457 << QString(" . . . ")
5458 << bool(insert_text);
5459 QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5460 << QString("000.000.000.000;#")
5463 << QString("###.###.###.###")
5464 << bool(insert_text);
5465 QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5466 << QString("000.000.000.000")
5467 << QString("1234123412341234")
5468 << QString("123.412.341.234")
5469 << QString("123.412.341.234")
5470 << bool(insert_text);
5471 QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5476 << bool(insert_text);
5477 QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5482 << bool(insert_text);
5484 QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5489 << bool(insert_text);
5490 QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5491 << QString("!A!A!A!A")
5495 << bool(insert_text);
5496 QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5501 << bool(insert_text);
5503 QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5504 << QString("{n}[0]")
5508 << bool(insert_text);
5509 QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5510 << QString("\\\\N\\\\n00")
5514 << bool(insert_text);
5515 QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5516 << QString("\\\\\\\\00")
5520 << bool(insert_text);
5521 QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5522 << QString("\\\\(00\\\\)")
5526 << bool(insert_text);
5528 QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5529 << QString(">AAAA<AAAA!AAAA")
5530 << QString("AbCdEfGhIjKl")
5531 << QString("ABCDefghIjKl")
5532 << QString("ABCDefghIjKl")
5533 << bool(insert_text);
5534 QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5535 << QString(">aaaa<aaaa!aaaa")
5536 << QString("AbCdEfGhIjKl")
5537 << QString("ABCDefghIjKl")
5538 << QString("ABCDefghIjKl")
5539 << bool(insert_text);
5541 QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5542 << QString(">A<A<A>A>A<A!A!A")
5543 << QString("AbCdEFGH")
5544 << QString("AbcDEfGH")
5545 << QString("AbcDEfGH")
5546 << bool(insert_text);
5547 QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5548 << QString(">A<A<A>A>A<A!A!A")
5549 << QString("aBcDefgh")
5550 << QString("AbcDEfgh")
5551 << QString("AbcDEfgh")
5552 << bool(insert_text);
5553 QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5554 << QString(">a<a<a>a>a<a!a!a")
5555 << QString("AbCdEFGH")
5556 << QString("AbcDEfGH")
5557 << QString("AbcDEfGH")
5558 << bool(insert_text);
5559 QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5560 << QString(">a<a<a>a>a<a!a!a")
5561 << QString("aBcDefgh")
5562 << QString("AbcDEfgh")
5563 << QString("AbcDEfgh")
5564 << bool(insert_text);
5565 QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5566 << QString(">H<H<H>H>H<H!H!H")
5567 << QString("aBcDef01")
5568 << QString("AbcDEf01")
5569 << QString("AbcDEf01")
5570 << bool(insert_text);
5571 QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5572 << QString(">h<h<h>h>h<h!h!h")
5573 << QString("aBcDef92")
5574 << QString("AbcDEf92")
5575 << QString("AbcDEf92")
5576 << bool(insert_text);
5578 QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5579 << QString("AAAAAAAA")
5580 << QString("A2#a;.0!")
5583 << bool(insert_text);
5584 QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5586 << QString("f4f4f4f4")
5589 << bool(insert_text);
5590 QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5591 << QString("9999;0")
5595 << bool(insert_text);
5599 void tst_qquicktextinput::setInputMask()
5601 QFETCH(QString, mask);
5602 QFETCH(QString, input);
5603 QFETCH(QString, expectedText);
5604 QFETCH(QString, expectedDisplay);
5605 QFETCH(bool, insert_text);
5607 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5608 QQmlComponent textInputComponent(&engine);
5609 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5610 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5611 QVERIFY(textInput != 0);
5613 // then either insert using insert() or keyboard
5615 textInput->insert(0, input);
5617 QQuickCanvas canvas;
5618 textInput->setParentItem(canvas.rootItem());
5620 canvas.requestActivateWindow();
5621 QTest::qWaitForWindowActive(&canvas);
5622 QVERIFY(textInput->hasActiveFocus());
5624 simulateKey(&canvas, Qt::Key_Home);
5625 for (int i = 0; i < input.length(); i++)
5626 QTest::keyClick(&canvas, input.at(i).toLatin1());
5629 QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5630 QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5632 QCOMPARE(textInput->text(), expectedText);
5633 QCOMPARE(textInput->displayText(), expectedDisplay);
5636 void tst_qquicktextinput::inputMask_data()
5638 QTest::addColumn<QString>("mask");
5639 QTest::addColumn<QString>("expectedMask");
5641 // if no mask is set a nul string should be returned
5642 QTest::newRow("nul 1") << QString("") << QString();
5643 QTest::newRow("nul 2") << QString() << QString();
5645 // try different masks
5646 QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5647 QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5648 QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5649 QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5651 // set an invalid input mask...
5652 // the current behaviour is that this exact (faulty) string is returned.
5653 QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5655 // verify that we can unset the mask again
5656 QTest::newRow("unset") << QString("") << QString();
5659 void tst_qquicktextinput::inputMask()
5661 QFETCH(QString, mask);
5662 QFETCH(QString, expectedMask);
5664 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5665 QQmlComponent textInputComponent(&engine);
5666 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5667 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5668 QVERIFY(textInput != 0);
5670 QCOMPARE(textInput->inputMask(), expectedMask);
5673 void tst_qquicktextinput::clearInputMask()
5675 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
5676 QQmlComponent textInputComponent(&engine);
5677 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5678 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5679 QVERIFY(textInput != 0);
5681 QVERIFY(textInput->inputMask() != QString());
5682 textInput->setInputMask(QString());
5683 QCOMPARE(textInput->inputMask(), QString());
5686 void tst_qquicktextinput::keypress_inputMask_data()
5688 QTest::addColumn<QString>("mask");
5689 QTest::addColumn<KeyList>("keys");
5690 QTest::addColumn<QString>("expectedText");
5691 QTest::addColumn<QString>("expectedDisplayText");
5695 // inserting 'A1.2B'
5696 keys << Qt::Key_Home << "A1.2B";
5697 QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
5702 keys << Qt::Key_Home << "0!P3";
5703 QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
5708 keys << Qt::Key_Home
5710 QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5714 // selecting all and delete
5715 keys << Qt::Key_Home
5716 << Key(Qt::ShiftModifier, Qt::Key_End)
5718 QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5722 // inserting '12.12' then two backspaces
5723 keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
5724 QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
5729 keys << Qt::Key_Home << "12ab";
5730 QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
5734 void tst_qquicktextinput::keypress_inputMask()
5736 QFETCH(QString, mask);
5737 QFETCH(KeyList, keys);
5738 QFETCH(QString, expectedText);
5739 QFETCH(QString, expectedDisplayText);
5741 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5742 QQmlComponent textInputComponent(&engine);
5743 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5744 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5745 QVERIFY(textInput != 0);
5747 QQuickCanvas canvas;
5748 textInput->setParentItem(canvas.rootItem());
5750 canvas.requestActivateWindow();
5751 QTest::qWaitForWindowActive(&canvas);
5752 QVERIFY(textInput->hasActiveFocus());
5754 simulateKeys(&canvas, keys);
5756 QCOMPARE(textInput->text(), expectedText);
5757 QCOMPARE(textInput->displayText(), expectedDisplayText);
5761 void tst_qquicktextinput::hasAcceptableInputMask_data()
5763 QTest::addColumn<QString>("optionalMask");
5764 QTest::addColumn<QString>("requiredMask");
5765 QTest::addColumn<QString>("invalid");
5766 QTest::addColumn<QString>("valid");
5768 QTest::newRow("Alphabetic optional and required")
5769 << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
5770 QTest::newRow("Alphanumeric optional and require")
5771 << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
5772 QTest::newRow("Any optional and required")
5773 << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
5774 QTest::newRow("Numeric (0-9) required")
5775 << QString("0000") << QString("9999") << QString("11") << QString("1138");
5776 QTest::newRow("Numeric (1-9) optional and required")
5777 << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
5780 void tst_qquicktextinput::hasAcceptableInputMask()
5782 QFETCH(QString, optionalMask);
5783 QFETCH(QString, requiredMask);
5784 QFETCH(QString, invalid);
5785 QFETCH(QString, valid);
5787 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + optionalMask + "\" }";
5788 QQmlComponent textInputComponent(&engine);
5789 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5790 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5791 QVERIFY(textInput != 0);
5793 // test that invalid input (for required) work for optionalMask
5794 textInput->setText(invalid);
5795 QVERIFY(textInput->hasAcceptableInput());
5797 // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
5798 QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
5800 // test requiredMask
5801 textInput->setInputMask(requiredMask);
5802 textInput->setText(invalid);
5803 QVERIFY(!textInput->hasAcceptableInput());
5805 textInput->setText(valid);
5806 QVERIFY(textInput->hasAcceptableInput());
5809 void tst_qquicktextinput::maskCharacter_data()
5811 QTest::addColumn<QString>("mask");
5812 QTest::addColumn<QString>("input");
5813 QTest::addColumn<bool>("expectedValid");
5815 QTest::newRow("Hex") << QString("H")
5816 << QString("0123456789abcdefABCDEF") << true;
5817 QTest::newRow("hex") << QString("h")
5818 << QString("0123456789abcdefABCDEF") << true;
5819 QTest::newRow("HexInvalid") << QString("H")
5820 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5822 QTest::newRow("hexInvalid") << QString("h")
5823 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5825 QTest::newRow("Bin") << QString("B")
5826 << QString("01") << true;
5827 QTest::newRow("bin") << QString("b")
5828 << QString("01") << true;
5829 QTest::newRow("BinInvalid") << QString("B")
5830 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5832 QTest::newRow("binInvalid") << QString("b")
5833 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5837 void tst_qquicktextinput::maskCharacter()
5839 QFETCH(QString, mask);
5840 QFETCH(QString, input);
5841 QFETCH(bool, expectedValid);
5843 QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: \"" + mask + "\" }";
5844 QQmlComponent textInputComponent(&engine);
5845 textInputComponent.setData(componentStr.toLatin1(), QUrl());
5846 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5847 QVERIFY(textInput != 0);
5849 for (int i = 0; i < input.size(); ++i) {
5850 QString in = QString(input.at(i));
5851 QString expected = expectedValid ? in : QString();
5852 textInput->setText(QString(input.at(i)));
5853 QCOMPARE(textInput->text(), expected);
5857 QTEST_MAIN(tst_qquicktextinput)
5859 #include "tst_qquicktextinput.moc"