1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/util.h"
44 #include <private/qinputpanel_p.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputPanel>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
57 #include <QtOpenGL/QGLShaderProgram>
61 #include <Carbon/Carbon.h>
64 #include "qplatformdefs.h"
65 #include "../../shared/platforminputcontext.h"
67 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
68 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
70 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
72 // XXX This will be replaced by some clever persistent platform image store.
73 QString persistent_dir = QDeclarativeDataTest::instance()->dataDirectory();
74 QString arch = "unknown-architecture"; // QTest needs to help with this.
76 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
78 if (!QFile::exists(expectfile)) {
79 actual.save(expectfile);
80 qWarning() << "created" << expectfile;
86 template <typename T> static T evaluate(QObject *scope, const QString &expression)
88 QDeclarativeExpression expr(qmlContext(scope), scope, expression);
89 T result = expr.evaluate().value<T>();
91 qWarning() << expr.error().toString();
95 typedef QPair<int, QChar> Key;
97 class tst_qquicktextinput : public QDeclarativeDataTest
102 tst_qquicktextinput();
112 void isRightToLeft_data();
113 void isRightToLeft();
114 void moveCursorSelection_data();
115 void moveCursorSelection();
116 void moveCursorSelectionSequence_data();
117 void moveCursorSelectionSequence();
118 void dragMouseSelection();
119 void mouseSelectionMode_data();
120 void mouseSelectionMode();
121 void tripleClickSelectsAll();
123 void horizontalAlignment_data();
124 void horizontalAlignment();
125 void horizontalAlignment_RightToLeft();
126 void verticalAlignment();
137 void passwordCharacter();
138 void cursorDelegate();
139 void cursorVisible();
140 void cursorRectangle();
142 void navigation_RTL();
144 void copyAndPasteKeySequence();
145 void canPasteEmpty();
149 void openInputPanel();
150 void setHAlignClearCache();
151 void focusOutClearSelection();
154 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
155 void passwordEchoDelay();
157 void geometrySignals();
158 void testQtQuick11Attributes();
159 void testQtQuick11Attributes_data();
161 void preeditAutoScroll();
162 void preeditCursorRectangle();
163 void inputContextMouseHandler();
164 void inputMethodComposing();
165 void inputPanelUpdate();
166 void cursorRectangleSize();
175 void keySequence_data();
182 void undo_keypressevents_data();
183 void undo_keypressevents();
186 void QTBUG_19956_data();
187 void QTBUG_19956_regexp();
189 void negativeDimensions();
192 void simulateKey(QQuickView *, int key);
194 void simulateKeys(QWindow *window, const QList<Key> &keys);
195 void simulateKeys(QWindow *window, const QKeySequence &sequence);
197 QDeclarativeEngine engine;
198 QStringList standard;
199 QStringList colorStrings;
202 typedef QList<int> IntList;
203 Q_DECLARE_METATYPE(IntList)
205 typedef QList<Key> KeyList;
206 Q_DECLARE_METATYPE(KeyList)
208 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
210 for (int i = 0; i < keys.count(); ++i) {
211 const int key = keys.at(i).first;
212 const int modifiers = key & Qt::KeyboardModifierMask;
213 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
215 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
216 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
218 QGuiApplication::sendEvent(window, &press);
219 QGuiApplication::sendEvent(window, &release);
223 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
225 for (int i = 0; i < sequence.count(); ++i) {
226 const int key = sequence[i];
227 const int modifiers = key & Qt::KeyboardModifierMask;
229 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
233 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
235 for (int i = 0; i < sequence.count(); ++i)
236 keys << Key(sequence[i], QChar());
240 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
242 for (int i = 0; i < N - 1; ++i) {
243 int key = QTest::asciiToKey(characters[i]);
244 QChar character = QLatin1Char(characters[i]);
245 keys << Key(key, character);
250 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
252 keys << Key(key, QChar());
256 void tst_qquicktextinput::cleanup()
258 // ensure not even skipped tests with custom input context leave it dangling
259 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
260 inputPanelPrivate->testContext = 0;
263 tst_qquicktextinput::tst_qquicktextinput()
265 standard << "the quick brown fox jumped over the lazy dog"
266 << "It's supercalifragisiticexpialidocious!"
271 colorStrings << "aliceblue"
285 void tst_qquicktextinput::text()
288 QDeclarativeComponent textinputComponent(&engine);
289 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
290 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
292 QVERIFY(textinputObject != 0);
293 QCOMPARE(textinputObject->text(), QString(""));
294 QCOMPARE(textinputObject->length(), 0);
296 delete textinputObject;
299 for (int i = 0; i < standard.size(); i++)
301 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
302 QDeclarativeComponent textinputComponent(&engine);
303 textinputComponent.setData(componentStr.toLatin1(), QUrl());
304 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
306 QVERIFY(textinputObject != 0);
307 QCOMPARE(textinputObject->text(), standard.at(i));
308 QCOMPARE(textinputObject->length(), standard.at(i).length());
310 delete textinputObject;
315 void tst_qquicktextinput::width()
317 // uses Font metrics to find the width for standard
319 QDeclarativeComponent textinputComponent(&engine);
320 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
321 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
323 QVERIFY(textinputObject != 0);
324 QCOMPARE(textinputObject->width(), 0.0);
326 delete textinputObject;
329 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
331 for (int i = 0; i < standard.size(); i++)
333 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
334 QDeclarativeComponent textinputComponent(&engine);
335 textinputComponent.setData(componentStr.toLatin1(), QUrl());
336 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
338 QString s = standard.at(i);
339 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
341 QTextLayout layout(s);
342 layout.setFont(textinputObject->font());
343 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
344 if (requiresUnhintedMetrics) {
346 option.setUseDesignMetrics(true);
347 layout.setTextOption(option);
350 layout.beginLayout();
352 QTextLine line = layout.createLine();
359 qreal metricWidth = ceil(layout.boundingRect().width());
361 QVERIFY(textinputObject != 0);
362 int delta = abs(int(int(textinputObject->width()) - metricWidth));
363 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
365 delete textinputObject;
369 void tst_qquicktextinput::font()
371 //test size, then bold, then italic, then family
373 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
374 QDeclarativeComponent textinputComponent(&engine);
375 textinputComponent.setData(componentStr.toLatin1(), QUrl());
376 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
378 QVERIFY(textinputObject != 0);
379 QCOMPARE(textinputObject->font().pointSize(), 40);
380 QCOMPARE(textinputObject->font().bold(), false);
381 QCOMPARE(textinputObject->font().italic(), false);
383 delete textinputObject;
387 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
388 QDeclarativeComponent textinputComponent(&engine);
389 textinputComponent.setData(componentStr.toLatin1(), QUrl());
390 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
392 QVERIFY(textinputObject != 0);
393 QCOMPARE(textinputObject->font().bold(), true);
394 QCOMPARE(textinputObject->font().italic(), false);
396 delete textinputObject;
400 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
401 QDeclarativeComponent textinputComponent(&engine);
402 textinputComponent.setData(componentStr.toLatin1(), QUrl());
403 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
405 QVERIFY(textinputObject != 0);
406 QCOMPARE(textinputObject->font().italic(), true);
407 QCOMPARE(textinputObject->font().bold(), false);
409 delete textinputObject;
413 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
414 QDeclarativeComponent textinputComponent(&engine);
415 textinputComponent.setData(componentStr.toLatin1(), QUrl());
416 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
418 QVERIFY(textinputObject != 0);
419 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
420 QCOMPARE(textinputObject->font().bold(), false);
421 QCOMPARE(textinputObject->font().italic(), false);
423 delete textinputObject;
427 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
428 QDeclarativeComponent textinputComponent(&engine);
429 textinputComponent.setData(componentStr.toLatin1(), QUrl());
430 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
432 QVERIFY(textinputObject != 0);
433 QCOMPARE(textinputObject->font().family(), QString(""));
435 delete textinputObject;
439 void tst_qquicktextinput::color()
442 for (int i = 0; i < colorStrings.size(); i++)
444 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
445 QDeclarativeComponent textinputComponent(&engine);
446 textinputComponent.setData(componentStr.toLatin1(), QUrl());
447 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
448 QVERIFY(textinputObject != 0);
449 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
451 delete textinputObject;
454 //test selection color
455 for (int i = 0; i < colorStrings.size(); i++)
457 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
458 QDeclarativeComponent textinputComponent(&engine);
459 textinputComponent.setData(componentStr.toLatin1(), QUrl());
460 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
461 QVERIFY(textinputObject != 0);
462 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
464 delete textinputObject;
467 //test selected text color
468 for (int i = 0; i < colorStrings.size(); i++)
470 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
471 QDeclarativeComponent textinputComponent(&engine);
472 textinputComponent.setData(componentStr.toLatin1(), QUrl());
473 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
474 QVERIFY(textinputObject != 0);
475 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
477 delete textinputObject;
481 QString colorStr = "#AA001234";
482 QColor testColor("#001234");
483 testColor.setAlpha(170);
485 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
486 QDeclarativeComponent textinputComponent(&engine);
487 textinputComponent.setData(componentStr.toLatin1(), QUrl());
488 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
490 QVERIFY(textinputObject != 0);
491 QCOMPARE(textinputObject->color(), testColor);
493 delete textinputObject;
497 void tst_qquicktextinput::wrap()
500 // for specified width and wrap set true
502 QDeclarativeComponent textComponent(&engine);
503 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
504 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
505 textHeight = textObject->height();
507 QVERIFY(textObject != 0);
508 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
509 QCOMPARE(textObject->width(), 300.);
514 for (int i = 0; i < standard.count(); i++) {
515 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
516 QDeclarativeComponent textComponent(&engine);
517 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
518 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
520 QVERIFY(textObject != 0);
521 QCOMPARE(textObject->width(), 30.);
522 QVERIFY(textObject->height() > textHeight);
524 int oldHeight = textObject->height();
525 textObject->setWidth(100);
526 QVERIFY(textObject->height() < oldHeight);
532 void tst_qquicktextinput::selection()
534 QString testStr = standard[0];
535 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
536 QDeclarativeComponent textinputComponent(&engine);
537 textinputComponent.setData(componentStr.toLatin1(), QUrl());
538 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
539 QVERIFY(textinputObject != 0);
542 //Test selection follows cursor
543 for (int i=0; i<= testStr.size(); i++) {
544 textinputObject->setCursorPosition(i);
545 QCOMPARE(textinputObject->cursorPosition(), i);
546 QCOMPARE(textinputObject->selectionStart(), i);
547 QCOMPARE(textinputObject->selectionEnd(), i);
548 QVERIFY(textinputObject->selectedText().isNull());
551 textinputObject->setCursorPosition(0);
552 QVERIFY(textinputObject->cursorPosition() == 0);
553 QVERIFY(textinputObject->selectionStart() == 0);
554 QVERIFY(textinputObject->selectionEnd() == 0);
555 QVERIFY(textinputObject->selectedText().isNull());
557 // Verify invalid positions are ignored.
558 textinputObject->setCursorPosition(-1);
559 QVERIFY(textinputObject->cursorPosition() == 0);
560 QVERIFY(textinputObject->selectionStart() == 0);
561 QVERIFY(textinputObject->selectionEnd() == 0);
562 QVERIFY(textinputObject->selectedText().isNull());
564 textinputObject->setCursorPosition(textinputObject->text().count()+1);
565 QVERIFY(textinputObject->cursorPosition() == 0);
566 QVERIFY(textinputObject->selectionStart() == 0);
567 QVERIFY(textinputObject->selectionEnd() == 0);
568 QVERIFY(textinputObject->selectedText().isNull());
571 for (int i=0; i<= testStr.size(); i++) {
572 textinputObject->select(0,i);
573 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
575 for (int i=0; i<= testStr.size(); i++) {
576 textinputObject->select(i,testStr.size());
577 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
580 textinputObject->setCursorPosition(0);
581 QVERIFY(textinputObject->cursorPosition() == 0);
582 QVERIFY(textinputObject->selectionStart() == 0);
583 QVERIFY(textinputObject->selectionEnd() == 0);
584 QVERIFY(textinputObject->selectedText().isNull());
586 //Test Error Ignoring behaviour
587 textinputObject->setCursorPosition(0);
588 QVERIFY(textinputObject->selectedText().isNull());
589 textinputObject->select(-10,0);
590 QVERIFY(textinputObject->selectedText().isNull());
591 textinputObject->select(100,110);
592 QVERIFY(textinputObject->selectedText().isNull());
593 textinputObject->select(0,-10);
594 QVERIFY(textinputObject->selectedText().isNull());
595 textinputObject->select(0,100);
596 QVERIFY(textinputObject->selectedText().isNull());
597 textinputObject->select(0,10);
598 QVERIFY(textinputObject->selectedText().size() == 10);
599 textinputObject->select(-10,10);
600 QVERIFY(textinputObject->selectedText().size() == 10);
601 textinputObject->select(100,101);
602 QVERIFY(textinputObject->selectedText().size() == 10);
603 textinputObject->select(0,-10);
604 QVERIFY(textinputObject->selectedText().size() == 10);
605 textinputObject->select(0,100);
606 QVERIFY(textinputObject->selectedText().size() == 10);
608 textinputObject->deselect();
609 QVERIFY(textinputObject->selectedText().isNull());
610 textinputObject->select(0,10);
611 QVERIFY(textinputObject->selectedText().size() == 10);
612 textinputObject->deselect();
613 QVERIFY(textinputObject->selectedText().isNull());
615 // test input method selection
616 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
617 textinputObject->setFocus(true);
619 QList<QInputMethodEvent::Attribute> attributes;
620 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
621 QInputMethodEvent event("", attributes);
622 QGuiApplication::sendEvent(textinputObject, &event);
624 QCOMPARE(selectionSpy.count(), 1);
625 QCOMPARE(textinputObject->selectionStart(), 12);
626 QCOMPARE(textinputObject->selectionEnd(), 17);
628 delete textinputObject;
631 void tst_qquicktextinput::isRightToLeft_data()
633 QTest::addColumn<QString>("text");
634 QTest::addColumn<bool>("emptyString");
635 QTest::addColumn<bool>("firstCharacter");
636 QTest::addColumn<bool>("lastCharacter");
637 QTest::addColumn<bool>("middleCharacter");
638 QTest::addColumn<bool>("startString");
639 QTest::addColumn<bool>("midString");
640 QTest::addColumn<bool>("endString");
642 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
643 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
644 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
645 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
646 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
647 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;
648 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
651 void tst_qquicktextinput::isRightToLeft()
653 QFETCH(QString, text);
654 QFETCH(bool, emptyString);
655 QFETCH(bool, firstCharacter);
656 QFETCH(bool, lastCharacter);
657 QFETCH(bool, middleCharacter);
658 QFETCH(bool, startString);
659 QFETCH(bool, midString);
660 QFETCH(bool, endString);
662 QQuickTextInput textInput;
663 textInput.setText(text);
665 // first test that the right string is delivered to the QString::isRightToLeft()
666 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
667 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
668 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
669 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
670 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
671 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
673 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
674 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
676 // then test that the feature actually works
677 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
678 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
679 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
680 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
681 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
682 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
684 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
685 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
688 void tst_qquicktextinput::moveCursorSelection_data()
690 QTest::addColumn<QString>("testStr");
691 QTest::addColumn<int>("cursorPosition");
692 QTest::addColumn<int>("movePosition");
693 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
694 QTest::addColumn<int>("selectionStart");
695 QTest::addColumn<int>("selectionEnd");
696 QTest::addColumn<bool>("reversible");
698 // () contains the text selected by the cursor.
699 // <> contains the actual selection.
701 QTest::newRow("(t)he|characters")
702 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
703 QTest::newRow("do(g)|characters")
704 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
705 QTest::newRow("jum(p)ed|characters")
706 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
707 QTest::newRow("jumped( )over|characters")
708 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
709 QTest::newRow("(the )|characters")
710 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
711 QTest::newRow("( dog)|characters")
712 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
713 QTest::newRow("( jumped )|characters")
714 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
715 QTest::newRow("th(e qu)ick|characters")
716 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
717 QTest::newRow("la(zy d)og|characters")
718 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
719 QTest::newRow("jum(ped ov)er|characters")
720 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
721 QTest::newRow("()the|characters")
722 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
723 QTest::newRow("dog()|characters")
724 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
725 QTest::newRow("jum()ped|characters")
726 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
728 QTest::newRow("<(t)he>|words")
729 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
730 QTest::newRow("<do(g)>|words")
731 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
732 QTest::newRow("<jum(p)ed>|words")
733 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
734 QTest::newRow("<jumped( )>over|words,ltr")
735 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
736 QTest::newRow("jumped<( )over>|words,rtl")
737 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
738 QTest::newRow("<(the )>quick|words,ltr")
739 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
740 QTest::newRow("<(the )quick>|words,rtl")
741 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
742 QTest::newRow("<lazy( dog)>|words,ltr")
743 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
744 QTest::newRow("lazy<( dog)>|words,rtl")
745 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
746 QTest::newRow("<fox( jumped )>over|words,ltr")
747 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
748 QTest::newRow("fox<( jumped )over>|words,rtl")
749 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
750 QTest::newRow("<th(e qu)ick>|words")
751 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
752 QTest::newRow("<la(zy d)og|words>")
753 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
754 QTest::newRow("<jum(ped ov)er>|words")
755 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
756 QTest::newRow("<()>the|words")
757 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
758 QTest::newRow("dog<()>|words")
759 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
760 QTest::newRow("jum<()>ped|words")
761 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
763 QTest::newRow("Hello<(,)> |words")
764 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
765 QTest::newRow("Hello<(, )>world|words,ltr")
766 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
767 QTest::newRow("Hello<(, )world>|words,rtl")
768 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
769 QTest::newRow("<Hel(lo, )>world|words,ltr")
770 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
771 QTest::newRow("<Hel(lo, )world>|words,rtl")
772 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
773 QTest::newRow("<Hel(lo)>,|words")
774 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
775 QTest::newRow("Hello<()>,|words")
776 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
777 QTest::newRow("Hello,<()>|words")
778 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
779 QTest::newRow("Hello<,( )>world|words,ltr")
780 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
781 QTest::newRow("Hello,<( )world>|words,rtl")
782 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
783 QTest::newRow("Hello<,( world)>|words,ltr")
784 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
785 QTest::newRow("Hello,<( world)>|words,rtl")
786 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
787 QTest::newRow("Hello<,( world!)>|words,ltr")
788 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
789 QTest::newRow("Hello,<( world!)>|words,rtl")
790 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
791 QTest::newRow("Hello<(, world!)>|words")
792 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
793 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
795 // QTest::newRow("world<(!)>|words")
796 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
797 QTest::newRow("world!<()>)|words")
798 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
799 QTest::newRow("world<()>!)|words")
800 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
802 QTest::newRow("<(,)>olleH |words")
803 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
804 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
805 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
806 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
807 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
808 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
809 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
810 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
811 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
812 QTest::newRow(",<(ol)leH>,|words")
813 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
814 QTest::newRow(",<()>olleH|words")
815 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
816 QTest::newRow("<()>,olleH|words")
817 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
818 QTest::newRow("<dlrow( )>,olleH|words,ltr")
819 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
820 QTest::newRow("dlrow<( ),>olleH|words,rtl")
821 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
822 QTest::newRow("<(dlrow )>,olleH|words,ltr")
823 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
824 QTest::newRow("<(dlrow ),>olleH|words,rtl")
825 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
826 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
827 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
828 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
829 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
830 QTest::newRow("(!dlrow ,)olleH|words")
831 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
832 QTest::newRow("<(!)>dlrow|words")
833 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
834 QTest::newRow("<()>!dlrow|words")
835 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
836 QTest::newRow("!<()>dlrow|words")
837 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
839 QTest::newRow(" <s(pac)ey> text |words")
840 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
841 QTest::newRow(" spacey <t(ex)t> |words")
842 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
843 QTest::newRow("<( )>spacey text |words|ltr")
844 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
845 QTest::newRow("<( )spacey> text |words|rtl")
846 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
847 QTest::newRow("spacey <text( )>|words|ltr")
848 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
850 // QTest::newRow("spacey text<( )>|words|rtl")
851 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
852 QTest::newRow("<()> spacey text |words")
853 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
854 QTest::newRow(" spacey text <()>|words")
855 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
858 void tst_qquicktextinput::moveCursorSelection()
860 QFETCH(QString, testStr);
861 QFETCH(int, cursorPosition);
862 QFETCH(int, movePosition);
863 QFETCH(QQuickTextInput::SelectionMode, mode);
864 QFETCH(int, selectionStart);
865 QFETCH(int, selectionEnd);
866 QFETCH(bool, reversible);
868 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
869 QDeclarativeComponent textinputComponent(&engine);
870 textinputComponent.setData(componentStr.toLatin1(), QUrl());
871 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
872 QVERIFY(textinputObject != 0);
874 textinputObject->setCursorPosition(cursorPosition);
875 textinputObject->moveCursorSelection(movePosition, mode);
877 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
878 QCOMPARE(textinputObject->selectionStart(), selectionStart);
879 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
882 textinputObject->setCursorPosition(movePosition);
883 textinputObject->moveCursorSelection(cursorPosition, mode);
885 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
886 QCOMPARE(textinputObject->selectionStart(), selectionStart);
887 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
890 delete textinputObject;
893 void tst_qquicktextinput::moveCursorSelectionSequence_data()
895 QTest::addColumn<QString>("testStr");
896 QTest::addColumn<int>("cursorPosition");
897 QTest::addColumn<int>("movePosition1");
898 QTest::addColumn<int>("movePosition2");
899 QTest::addColumn<int>("selection1Start");
900 QTest::addColumn<int>("selection1End");
901 QTest::addColumn<int>("selection2Start");
902 QTest::addColumn<int>("selection2End");
904 // () contains the text selected by the cursor.
905 // <> contains the actual selection.
906 // ^ is the revised cursor position.
907 // {} contains the revised selection.
909 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
914 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
919 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
924 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
929 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
934 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
939 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
944 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
949 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
954 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
959 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
964 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
969 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
974 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
979 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
984 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
989 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
994 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1000 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1005 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1011 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1016 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1022 QTest::newRow("{<(^} sp)acey> text |ltr")
1027 QTest::newRow("{<( ^}sp)acey> text |ltr")
1032 QTest::newRow("<( {s^p)acey>} text |rtl")
1037 QTest::newRow("<( {^sp)acey>} text |rtl")
1043 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1049 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1051 // << 15 << 12 << 14
1054 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1060 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
1062 // << 12 << 15 << 14
1067 void tst_qquicktextinput::moveCursorSelectionSequence()
1069 QFETCH(QString, testStr);
1070 QFETCH(int, cursorPosition);
1071 QFETCH(int, movePosition1);
1072 QFETCH(int, movePosition2);
1073 QFETCH(int, selection1Start);
1074 QFETCH(int, selection1End);
1075 QFETCH(int, selection2Start);
1076 QFETCH(int, selection2End);
1078 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1079 QDeclarativeComponent textinputComponent(&engine);
1080 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1081 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1082 QVERIFY(textinputObject != 0);
1084 textinputObject->setCursorPosition(cursorPosition);
1086 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1087 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1088 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1089 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1091 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1092 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1093 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1094 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1096 delete textinputObject;
1099 void tst_qquicktextinput::dragMouseSelection()
1101 QString qmlfile = testFile("mouseselection_true.qml");
1103 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1106 canvas.requestActivateWindow();
1107 QTest::qWaitForWindowShown(&canvas);
1109 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1111 QVERIFY(canvas.rootObject() != 0);
1112 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1113 QVERIFY(textInputObject != 0);
1115 // press-and-drag-and-release from x1 to x2
1118 int y = textInputObject->height()/2;
1119 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1120 QTest::mouseMove(&canvas, QPoint(x2, y));
1121 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1124 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1125 QVERIFY(str1.length() > 3);
1127 // press and drag the current selection.
1130 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1131 QTest::mouseMove(&canvas, QPoint(x2, y));
1132 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1134 QString str2 = textInputObject->selectedText();
1135 QVERIFY(str2.length() > 3);
1137 QVERIFY(str1 != str2);
1140 void tst_qquicktextinput::mouseSelectionMode_data()
1142 QTest::addColumn<QString>("qmlfile");
1143 QTest::addColumn<bool>("selectWords");
1146 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1147 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1148 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1151 void tst_qquicktextinput::mouseSelectionMode()
1153 QFETCH(QString, qmlfile);
1154 QFETCH(bool, selectWords);
1156 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1158 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1161 canvas.requestActivateWindow();
1162 QTest::qWaitForWindowShown(&canvas);
1163 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1165 QVERIFY(canvas.rootObject() != 0);
1166 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1167 QVERIFY(textInputObject != 0);
1169 // press-and-drag-and-release from x1 to x2
1172 int y = textInputObject->height()/2;
1173 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1174 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1175 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1178 QTRY_COMPARE(textInputObject->selectedText(), text);
1180 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1181 QVERIFY(textInputObject->selectedText() != text);
1185 void tst_qquicktextinput::horizontalAlignment_data()
1187 QTest::addColumn<int>("hAlign");
1188 QTest::addColumn<QString>("expectfile");
1190 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1191 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1192 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1195 void tst_qquicktextinput::horizontalAlignment()
1197 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1199 QFETCH(int, hAlign);
1200 QFETCH(QString, expectfile);
1202 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1205 canvas.requestActivateWindow();
1206 QTest::qWaitForWindowShown(&canvas);
1207 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1208 QObject *ob = canvas.rootObject();
1210 ob->setProperty("horizontalAlignment",hAlign);
1211 QImage actual = canvas.grabFrameBuffer();
1213 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1215 QImage expect(expectfile);
1217 QCOMPARE(actual,expect);
1220 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1222 PlatformInputContext platformInputContext;
1223 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
1224 inputPanelPrivate->testContext = &platformInputContext;
1226 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1227 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1228 QVERIFY(textInput != 0);
1231 const QString rtlText = textInput->text();
1233 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1234 QVERIFY(textInputPrivate != 0);
1235 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1236 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1238 // implicit alignment should follow the reading direction of RTL text
1239 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1240 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1241 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1242 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1244 // explicitly left aligned
1245 textInput->setHAlign(QQuickTextInput::AlignLeft);
1246 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1247 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1248 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1250 // explicitly right aligned
1251 textInput->setHAlign(QQuickTextInput::AlignRight);
1252 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1253 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1254 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1255 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1257 // explicitly center aligned
1258 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1259 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1260 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1261 QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0);
1262 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width());
1264 // reseted alignment should go back to following the text reading direction
1265 textInput->resetHAlign();
1266 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1267 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1268 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1269 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1271 // mirror the text item
1272 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1274 // mirrored implicit alignment should continue to follow the reading direction of the text
1275 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1276 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1277 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1278 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1280 // explicitly right aligned behaves as left aligned
1281 textInput->setHAlign(QQuickTextInput::AlignRight);
1282 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1283 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1284 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1286 // mirrored explicitly left aligned behaves as right aligned
1287 textInput->setHAlign(QQuickTextInput::AlignLeft);
1288 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1289 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1290 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1291 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1293 // disable mirroring
1294 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1295 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1296 textInput->resetHAlign();
1298 // English text should be implicitly left aligned
1299 textInput->setText("Hello world!");
1300 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1301 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1303 canvas.requestActivateWindow();
1304 QTest::qWaitForWindowShown(&canvas);
1305 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1307 // If there is no commited text, the preedit text should determine the alignment.
1308 textInput->setText(QString());
1309 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1310 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1311 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1312 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1314 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1315 // redundant as an actual input method may take care of it.
1316 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1318 // empty text with implicit alignment follows the system locale-based
1319 // keyboard input direction from QInputPanel::inputDirection()
1320 textInput->setText("");
1321 platformInputContext.setInputDirection(Qt::LeftToRight);
1322 QVERIFY(qApp->inputPanel()->inputDirection() == Qt::LeftToRight);
1323 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1324 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1326 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1327 platformInputContext.setInputDirection(Qt::RightToLeft);
1328 QVERIFY(qApp->inputPanel()->inputDirection() == Qt::RightToLeft);
1329 QCOMPARE(cursorRectangleSpy.count(), 1);
1330 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1331 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1332 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1334 // set input direction while having content
1335 platformInputContext.setInputDirection(Qt::LeftToRight);
1336 textInput->setText("a");
1337 platformInputContext.setInputDirection(Qt::RightToLeft);
1338 QTest::keyClick(&canvas, Qt::Key_Backspace);
1339 QVERIFY(textInput->text().isEmpty());
1340 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1341 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1342 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1344 // input direction changed while not having focus
1345 platformInputContext.setInputDirection(Qt::LeftToRight);
1346 textInput->setFocus(false);
1347 platformInputContext.setInputDirection(Qt::RightToLeft);
1348 textInput->setFocus(true);
1349 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1350 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1351 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1353 textInput->setHAlign(QQuickTextInput::AlignRight);
1354 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1355 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1356 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1359 void tst_qquicktextinput::verticalAlignment()
1361 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1362 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1363 QVERIFY(textInput != 0);
1366 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1367 QVERIFY(textInputPrivate != 0);
1369 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1370 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2);
1373 textInput->setVAlign(QQuickTextInput::AlignBottom);
1374 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1375 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2);
1377 // explicitly center aligned
1378 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1379 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1380 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2);
1381 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2);
1384 void tst_qquicktextinput::boundingRect()
1386 QDeclarativeComponent component(&engine);
1387 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1388 QScopedPointer<QObject> object(component.create());
1389 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1392 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1393 QCOMPARE(input->height(), input->boundingRect().height());
1395 input->setText("Hello World");
1396 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1397 QCOMPARE(input->height(), input->boundingRect().height());
1399 // bounding rect shouldn't exceed the size of the item, expect for the cursor width;
1400 input->setWidth(input->width() / 2);
1401 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1402 QCOMPARE(input->height(), input->boundingRect().height());
1404 input->setHeight(input->height() * 2);
1405 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1406 QCOMPARE(input->height(), input->boundingRect().height());
1408 QDeclarativeComponent cursorComponent(&engine);
1409 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1411 input->setCursorDelegate(&cursorComponent);
1413 // If a cursor delegate is used it's size should determine the excess width.
1414 QCOMPARE(input->width() + 8, input->boundingRect().width());
1415 QCOMPARE(input->height(), input->boundingRect().height());
1418 void tst_qquicktextinput::positionAt()
1420 QQuickView canvas(testFileUrl("positionAt.qml"));
1421 QVERIFY(canvas.rootObject() != 0);
1423 canvas.requestActivateWindow();
1424 QTest::qWaitForWindowShown(&canvas);
1426 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1427 QVERIFY(textinputObject != 0);
1429 // Check autoscrolled...
1431 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1433 QTextLayout layout(textinputObject->text());
1434 layout.setFont(textinputObject->font());
1436 if (!qmlDisableDistanceField()) {
1438 option.setUseDesignMetrics(true);
1439 layout.setTextOption(option);
1441 layout.beginLayout();
1442 QTextLine line = layout.createLine();
1445 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1446 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1447 int textWidth = floor(line.horizontalAdvance());
1449 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1450 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1452 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1453 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1454 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1456 // Check without autoscroll...
1457 textinputObject->setAutoScroll(false);
1458 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1460 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1461 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1463 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1464 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1466 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1467 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1468 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1470 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1471 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1473 QString preeditText = textinputObject->text().mid(0, pos);
1474 textinputObject->setText(textinputObject->text().mid(pos));
1475 textinputObject->setCursorPosition(0);
1477 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1478 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1480 // Check all points within the preedit text return the same position.
1481 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1482 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1483 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1485 // Verify positioning returns to normal after the preedit text.
1486 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1487 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1489 { QInputMethodEvent inputEvent;
1490 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1493 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1495 const qreal y0 = line.height() / 2;
1496 const qreal y1 = line.height() * 3 / 2;
1498 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1499 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1501 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1502 QVERIFY(newLinePos > pos);
1503 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1506 void tst_qquicktextinput::maxLength()
1508 QQuickView canvas(testFileUrl("maxLength.qml"));
1509 QVERIFY(canvas.rootObject() != 0);
1511 canvas.requestActivateWindow();
1512 QTest::qWaitForWindowShown(&canvas);
1514 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1515 QVERIFY(textinputObject != 0);
1516 QVERIFY(textinputObject->text().isEmpty());
1517 QVERIFY(textinputObject->maxLength() == 10);
1518 foreach (const QString &str, standard) {
1519 QVERIFY(textinputObject->text().length() <= 10);
1520 textinputObject->setText(str);
1521 QVERIFY(textinputObject->text().length() <= 10);
1524 textinputObject->setText("");
1525 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1526 for (int i=0; i<20; i++) {
1527 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1528 //simulateKey(&canvas, Qt::Key_A);
1529 QTest::keyPress(&canvas, Qt::Key_A);
1530 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1535 void tst_qquicktextinput::masks()
1537 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1538 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1539 QQuickView canvas(testFileUrl("masks.qml"));
1541 canvas.requestActivateWindow();
1542 QVERIFY(canvas.rootObject() != 0);
1543 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1544 QVERIFY(textinputObject != 0);
1545 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1546 QVERIFY(textinputObject->text().length() == 0);
1547 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1548 QCOMPARE(textinputObject->length(), 8);
1549 for (int i=0; i<10; i++) {
1550 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1551 QCOMPARE(textinputObject->length(), 8);
1552 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1553 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1554 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1555 //simulateKey(&canvas, Qt::Key_A);
1556 QTest::keyPress(&canvas, Qt::Key_A);
1557 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1562 void tst_qquicktextinput::validators()
1564 // Note that this test assumes that the validators are working properly
1565 // so you may need to run their tests first. All validators are checked
1566 // here to ensure that their exposure to QML is working.
1568 QLocale::setDefault(QLocale(QStringLiteral("C")));
1570 QQuickView canvas(testFileUrl("validators.qml"));
1572 canvas.requestActivateWindow();
1574 QVERIFY(canvas.rootObject() != 0);
1576 QLocale defaultLocale;
1577 QLocale enLocale("en");
1578 QLocale deLocale("de_DE");
1580 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1582 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1584 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1585 QVERIFY(intValidator);
1586 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1587 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1588 intValidator->setLocaleName(enLocale.name());
1589 QCOMPARE(intValidator->localeName(), enLocale.name());
1590 QCOMPARE(intInput->validator()->locale(), enLocale);
1591 intValidator->resetLocaleName();
1592 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1593 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1595 intInput->setFocus(true);
1596 QTRY_VERIFY(intInput->hasActiveFocus());
1597 QCOMPARE(intInput->hasAcceptableInput(), false);
1598 QCOMPARE(intInput->property("acceptable").toBool(), false);
1599 QTest::keyPress(&canvas, Qt::Key_1);
1600 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1602 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1603 QCOMPARE(intInput->hasAcceptableInput(), false);
1604 QCOMPARE(intInput->property("acceptable").toBool(), false);
1605 QCOMPARE(intSpy.count(), 0);
1606 QTest::keyPress(&canvas, Qt::Key_2);
1607 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1609 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1610 QCOMPARE(intInput->hasAcceptableInput(), false);
1611 QCOMPARE(intInput->property("acceptable").toBool(), false);
1612 QCOMPARE(intSpy.count(), 0);
1613 QTest::keyPress(&canvas, Qt::Key_Period);
1614 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1616 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1617 QCOMPARE(intInput->hasAcceptableInput(), false);
1618 QTest::keyPress(&canvas, Qt::Key_Comma);
1619 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1621 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1622 QCOMPARE(intInput->hasAcceptableInput(), false);
1623 QTest::keyPress(&canvas, Qt::Key_Backspace);
1624 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1626 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1627 QCOMPARE(intInput->hasAcceptableInput(), false);
1628 intValidator->setLocaleName(deLocale.name());
1629 QTest::keyPress(&canvas, Qt::Key_Period);
1630 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1632 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1633 QCOMPARE(intInput->hasAcceptableInput(), false);
1634 QTest::keyPress(&canvas, Qt::Key_Backspace);
1635 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1637 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1638 QCOMPARE(intInput->hasAcceptableInput(), false);
1639 intValidator->resetLocaleName();
1640 QTest::keyPress(&canvas, Qt::Key_1);
1641 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1643 QCOMPARE(intInput->text(), QLatin1String("11"));
1644 QCOMPARE(intInput->hasAcceptableInput(), true);
1645 QCOMPARE(intInput->property("acceptable").toBool(), true);
1646 QCOMPARE(intSpy.count(), 1);
1647 QTest::keyPress(&canvas, Qt::Key_0);
1648 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1650 QCOMPARE(intInput->text(), QLatin1String("11"));
1651 QCOMPARE(intInput->hasAcceptableInput(), true);
1652 QCOMPARE(intInput->property("acceptable").toBool(), true);
1653 QCOMPARE(intSpy.count(), 1);
1655 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1657 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1659 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1660 QVERIFY(dblValidator);
1661 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1662 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1663 dblValidator->setLocaleName(enLocale.name());
1664 QCOMPARE(dblValidator->localeName(), enLocale.name());
1665 QCOMPARE(dblInput->validator()->locale(), enLocale);
1666 dblValidator->resetLocaleName();
1667 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1668 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1670 dblInput->setFocus(true);
1671 QVERIFY(dblInput->hasActiveFocus() == true);
1672 QCOMPARE(dblInput->hasAcceptableInput(), false);
1673 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1674 QTest::keyPress(&canvas, Qt::Key_1);
1675 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1677 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1678 QCOMPARE(dblInput->hasAcceptableInput(), false);
1679 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1680 QCOMPARE(dblSpy.count(), 0);
1681 QTest::keyPress(&canvas, Qt::Key_2);
1682 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1684 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1685 QCOMPARE(dblInput->hasAcceptableInput(), true);
1686 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1687 QCOMPARE(dblSpy.count(), 1);
1688 QTest::keyPress(&canvas, Qt::Key_Comma);
1689 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1691 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1692 QCOMPARE(dblInput->hasAcceptableInput(), true);
1693 QTest::keyPress(&canvas, Qt::Key_1);
1694 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1696 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1697 QCOMPARE(dblInput->hasAcceptableInput(), true);
1698 dblValidator->setLocaleName(deLocale.name());
1699 QCOMPARE(dblInput->hasAcceptableInput(), true);
1700 QTest::keyPress(&canvas, Qt::Key_1);
1701 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1703 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1704 QCOMPARE(dblInput->hasAcceptableInput(), true);
1705 QTest::keyPress(&canvas, Qt::Key_1);
1706 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1708 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1709 QCOMPARE(dblInput->hasAcceptableInput(), true);
1710 QTest::keyPress(&canvas, Qt::Key_Backspace);
1711 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1713 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1714 QCOMPARE(dblInput->hasAcceptableInput(), true);
1715 QTest::keyPress(&canvas, Qt::Key_Backspace);
1716 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1718 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1719 QCOMPARE(dblInput->hasAcceptableInput(), true);
1720 QTest::keyPress(&canvas, Qt::Key_Backspace);
1721 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1723 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1724 QCOMPARE(dblInput->hasAcceptableInput(), true);
1725 dblValidator->resetLocaleName();
1726 QTest::keyPress(&canvas, Qt::Key_Period);
1727 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1729 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1730 QCOMPARE(dblInput->hasAcceptableInput(), true);
1731 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1732 QCOMPARE(dblSpy.count(), 1);
1733 QTest::keyPress(&canvas, Qt::Key_1);
1734 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1736 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1737 QCOMPARE(dblInput->hasAcceptableInput(), true);
1738 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1739 QCOMPARE(dblSpy.count(), 1);
1740 QTest::keyPress(&canvas, Qt::Key_1);
1741 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1743 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1744 QCOMPARE(dblInput->hasAcceptableInput(), true);
1745 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1746 QCOMPARE(dblSpy.count(), 1);
1747 QTest::keyPress(&canvas, Qt::Key_1);
1748 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1750 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1751 QCOMPARE(dblInput->hasAcceptableInput(), true);
1752 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1753 QCOMPARE(dblSpy.count(), 1);
1755 // Ensure the validator doesn't prevent characters being removed.
1756 dblInput->setValidator(intInput->validator());
1757 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1758 QCOMPARE(dblInput->hasAcceptableInput(), false);
1759 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1760 QCOMPARE(dblSpy.count(), 2);
1761 QTest::keyPress(&canvas, Qt::Key_Backspace);
1762 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1764 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1765 QCOMPARE(dblInput->hasAcceptableInput(), false);
1766 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1767 QCOMPARE(dblSpy.count(), 2);
1768 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1769 QTest::keyPress(&canvas, Qt::Key_1);
1770 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1772 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1773 QCOMPARE(dblInput->hasAcceptableInput(), false);
1774 QCOMPARE(dblSpy.count(), 2);
1775 QTest::keyPress(&canvas, Qt::Key_Backspace);
1776 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1778 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1779 QCOMPARE(dblInput->hasAcceptableInput(), false);
1780 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1781 QCOMPARE(dblSpy.count(), 2);
1782 QTest::keyPress(&canvas, Qt::Key_Backspace);
1783 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1785 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1786 QCOMPARE(dblInput->hasAcceptableInput(), false);
1787 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1788 QCOMPARE(dblSpy.count(), 2);
1789 QTest::keyPress(&canvas, Qt::Key_Backspace);
1790 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1792 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1793 QCOMPARE(dblInput->hasAcceptableInput(), false);
1794 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1795 QCOMPARE(dblSpy.count(), 2);
1796 QTest::keyPress(&canvas, Qt::Key_Backspace);
1797 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1799 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1800 QCOMPARE(dblInput->hasAcceptableInput(), false);
1801 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1802 QCOMPARE(dblSpy.count(), 2);
1803 QTest::keyPress(&canvas, Qt::Key_1);
1804 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1806 QCOMPARE(dblInput->text(), QLatin1String("11"));
1807 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1808 QCOMPARE(dblInput->hasAcceptableInput(), true);
1809 QCOMPARE(dblSpy.count(), 3);
1811 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1813 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
1814 strInput->setFocus(true);
1815 QVERIFY(strInput->hasActiveFocus() == true);
1816 QCOMPARE(strInput->hasAcceptableInput(), false);
1817 QCOMPARE(strInput->property("acceptable").toBool(), false);
1818 QTest::keyPress(&canvas, Qt::Key_1);
1819 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1821 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1822 QCOMPARE(strInput->hasAcceptableInput(), false);
1823 QCOMPARE(strInput->property("acceptable").toBool(), false);
1824 QCOMPARE(strSpy.count(), 0);
1825 QTest::keyPress(&canvas, Qt::Key_A);
1826 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1828 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1829 QCOMPARE(strInput->hasAcceptableInput(), false);
1830 QCOMPARE(strInput->property("acceptable").toBool(), false);
1831 QCOMPARE(strSpy.count(), 0);
1832 QTest::keyPress(&canvas, Qt::Key_A);
1833 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1835 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1836 QCOMPARE(strInput->hasAcceptableInput(), true);
1837 QCOMPARE(strInput->property("acceptable").toBool(), true);
1838 QCOMPARE(strSpy.count(), 1);
1839 QTest::keyPress(&canvas, Qt::Key_A);
1840 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1842 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1843 QCOMPARE(strInput->hasAcceptableInput(), true);
1844 QCOMPARE(strInput->property("acceptable").toBool(), true);
1845 QCOMPARE(strSpy.count(), 1);
1846 QTest::keyPress(&canvas, Qt::Key_A);
1847 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1849 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1850 QCOMPARE(strInput->hasAcceptableInput(), true);
1851 QCOMPARE(strInput->property("acceptable").toBool(), true);
1852 QCOMPARE(strSpy.count(), 1);
1853 QTest::keyPress(&canvas, Qt::Key_A);
1854 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1856 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1857 QCOMPARE(strInput->hasAcceptableInput(), true);
1858 QCOMPARE(strInput->property("acceptable").toBool(), true);
1859 QCOMPARE(strSpy.count(), 1);
1861 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
1862 QVERIFY(unvalidatedInput);
1863 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
1864 unvalidatedInput->setFocus(true);
1865 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
1866 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1867 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1868 QTest::keyPress(&canvas, Qt::Key_1);
1869 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1871 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
1872 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1873 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1874 QCOMPARE(unvalidatedSpy.count(), 0);
1875 QTest::keyPress(&canvas, Qt::Key_A);
1876 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1878 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
1879 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1880 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1881 QCOMPARE(unvalidatedSpy.count(), 0);
1884 void tst_qquicktextinput::inputMethods()
1886 QQuickView canvas(testFileUrl("inputmethods.qml"));
1888 canvas.requestActivateWindow();
1889 QTest::qWaitForWindowShown(&canvas);
1891 // test input method hints
1892 QVERIFY(canvas.rootObject() != 0);
1893 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1894 QVERIFY(input != 0);
1895 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1896 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1897 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1899 input->setFocus(true);
1900 QVERIFY(input->hasActiveFocus() == true);
1901 // test that input method event is committed
1902 QInputMethodEvent event;
1903 event.setCommitString( "My ", -12, 0);
1904 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1905 QCOMPARE(input->text(), QString("My Hello world!"));
1907 input->setCursorPosition(2);
1908 event.setCommitString("Your", -2, 2);
1909 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1910 QCOMPARE(input->text(), QString("Your Hello world!"));
1911 QCOMPARE(input->cursorPosition(), 4);
1913 input->setCursorPosition(7);
1914 event.setCommitString("Goodbye", -2, 5);
1915 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1916 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1917 QCOMPARE(input->cursorPosition(), 12);
1919 input->setCursorPosition(8);
1920 event.setCommitString("Our", -8, 4);
1921 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1922 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1923 QCOMPARE(input->cursorPosition(), 7);
1925 // test that basic tentative commit gets to text property on preedit state
1927 QList<QInputMethodEvent::Attribute> attributes;
1928 QInputMethodEvent preeditEvent("test", attributes);
1929 preeditEvent.setTentativeCommitString("test");
1930 QGuiApplication::sendEvent(input, &preeditEvent);
1931 QCOMPARE(input->text(), QString("test"));
1933 // tentative commit not allowed present in surrounding text
1934 QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
1935 QGuiApplication::sendEvent(input, &queryEvent);
1936 QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
1938 // if text with tentative commit does not validate, not allowed to be part of text property
1939 input->setText(""); // ensure input state is reset
1940 QValidator *validator = new QIntValidator(0, 100);
1941 input->setValidator(validator);
1942 QGuiApplication::sendEvent(input, &preeditEvent);
1943 QCOMPARE(input->text(), QString(""));
1944 input->setValidator(0);
1947 // input should reset selection even if replacement parameters are out of bounds
1948 input->setText("text");
1949 input->setCursorPosition(0);
1950 input->moveCursorSelection(input->text().length());
1951 event.setCommitString("replacement", -input->text().length(), input->text().length());
1952 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1953 QCOMPARE(input->selectionStart(), input->selectionEnd());
1955 QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
1956 QGuiApplication::sendEvent(input, &enabledQueryEvent);
1957 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
1959 input->setReadOnly(true);
1960 QGuiApplication::sendEvent(input, &enabledQueryEvent);
1961 QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
1965 TextInput element should only handle left/right keys until the cursor reaches
1966 the extent of the text, then they should ignore the keys.
1969 void tst_qquicktextinput::navigation()
1971 QQuickView canvas(testFileUrl("navigation.qml"));
1973 canvas.requestActivateWindow();
1975 QVERIFY(canvas.rootObject() != 0);
1977 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1979 QVERIFY(input != 0);
1980 input->setCursorPosition(0);
1981 QTRY_VERIFY(input->hasActiveFocus() == true);
1982 simulateKey(&canvas, Qt::Key_Left);
1983 QVERIFY(input->hasActiveFocus() == false);
1984 simulateKey(&canvas, Qt::Key_Right);
1985 QVERIFY(input->hasActiveFocus() == true);
1986 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1987 input->setCursorPosition(input->text().length());
1988 input->select(0,input->text().length());
1989 QVERIFY(input->selectionStart() != input->selectionEnd());
1990 simulateKey(&canvas, Qt::Key_Right);
1991 QVERIFY(input->selectionStart() == input->selectionEnd());
1992 QVERIFY(input->selectionStart() == input->text().length());
1993 QVERIFY(input->hasActiveFocus() == true);
1994 simulateKey(&canvas, Qt::Key_Right);
1995 QVERIFY(input->hasActiveFocus() == false);
1996 simulateKey(&canvas, Qt::Key_Left);
1997 QVERIFY(input->hasActiveFocus() == true);
1999 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2000 input->setCursorPosition(2);
2001 QCOMPARE(input->cursorPosition(),2);
2002 simulateKey(&canvas, Qt::Key_Up);
2003 QCOMPARE(input->cursorPosition(),2);
2004 simulateKey(&canvas, Qt::Key_Down);
2005 QCOMPARE(input->cursorPosition(),2);
2008 void tst_qquicktextinput::navigation_RTL()
2010 QQuickView canvas(testFileUrl("navigation.qml"));
2012 canvas.requestActivateWindow();
2014 QVERIFY(canvas.rootObject() != 0);
2016 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2018 QVERIFY(input != 0);
2019 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2020 input->setText(QString::fromUtf16(arabic_str, 11));
2022 input->setCursorPosition(0);
2023 QTRY_VERIFY(input->hasActiveFocus() == true);
2026 simulateKey(&canvas, Qt::Key_Right);
2027 QVERIFY(input->hasActiveFocus() == false);
2030 simulateKey(&canvas, Qt::Key_Left);
2031 QVERIFY(input->hasActiveFocus() == true);
2033 input->setCursorPosition(input->text().length());
2034 QVERIFY(input->hasActiveFocus() == true);
2037 simulateKey(&canvas, Qt::Key_Left);
2038 QVERIFY(input->hasActiveFocus() == false);
2041 simulateKey(&canvas, Qt::Key_Right);
2042 QVERIFY(input->hasActiveFocus() == true);
2045 void tst_qquicktextinput::copyAndPaste() {
2046 #ifndef QT_NO_CLIPBOARD
2050 PasteboardRef pasteboard;
2051 OSStatus status = PasteboardCreate(0, &pasteboard);
2052 if (status == noErr)
2053 CFRelease(pasteboard);
2055 QSKIP("This machine doesn't support the clipboard");
2059 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2060 QDeclarativeComponent textInputComponent(&engine);
2061 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2062 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2063 QVERIFY(textInput != 0);
2066 QCOMPARE(textInput->text().length(), 12);
2067 textInput->select(0, textInput->text().length());;
2069 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2070 QCOMPARE(textInput->selectedText().length(), 12);
2071 textInput->setCursorPosition(0);
2072 QVERIFY(textInput->canPaste());
2074 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2075 QCOMPARE(textInput->text().length(), 24);
2078 QVERIFY(textInput->canPaste());
2079 textInput->setReadOnly(true);
2080 QVERIFY(!textInput->canPaste());
2081 textInput->setReadOnly(false);
2082 QVERIFY(textInput->canPaste());
2085 textInput->setCursorPosition(0);
2086 textInput->selectWord();
2087 QCOMPARE(textInput->selectedText(), QString("Hello"));
2089 // select all and cut
2090 textInput->selectAll();
2092 QCOMPARE(textInput->text().length(), 0);
2094 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2095 QCOMPARE(textInput->text().length(), 24);
2097 // clear copy buffer
2098 QClipboard *clipboard = QGuiApplication::clipboard();
2101 QVERIFY(!textInput->canPaste());
2103 // test that copy functionality is disabled
2104 // when echo mode is set to hide text/password mode
2107 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2108 textInput->setEchoMode(echoMode);
2109 textInput->setText("My password");
2110 textInput->select(0, textInput->text().length());;
2112 if (echoMode == QQuickTextInput::Normal) {
2113 QVERIFY(!clipboard->text().isEmpty());
2114 QCOMPARE(clipboard->text(), QString("My password"));
2117 QVERIFY(clipboard->text().isEmpty());
2126 void tst_qquicktextinput::copyAndPasteKeySequence() {
2127 #ifndef QT_NO_CLIPBOARD
2131 PasteboardRef pasteboard;
2132 OSStatus status = PasteboardCreate(0, &pasteboard);
2133 if (status == noErr)
2134 CFRelease(pasteboard);
2136 QSKIP("This machine doesn't support the clipboard");
2140 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2141 QDeclarativeComponent textInputComponent(&engine);
2142 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2143 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2144 QVERIFY(textInput != 0);
2146 QQuickCanvas canvas;
2147 textInput->setParentItem(canvas.rootItem());
2149 canvas.requestActivateWindow();
2150 QTest::qWaitForWindowShown(&canvas);
2151 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2154 QVERIFY(textInput->hasActiveFocus());
2155 QCOMPARE(textInput->text().length(), 12);
2156 textInput->select(0, textInput->text().length());
2157 simulateKeys(&canvas, QKeySequence::Copy);
2158 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2159 QCOMPARE(textInput->selectedText().length(), 12);
2160 textInput->setCursorPosition(0);
2161 QVERIFY(textInput->canPaste());
2162 simulateKeys(&canvas, QKeySequence::Paste);
2163 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2164 QCOMPARE(textInput->text().length(), 24);
2166 // select all and cut
2167 simulateKeys(&canvas, QKeySequence::SelectAll);
2168 simulateKeys(&canvas, QKeySequence::Cut);
2169 QCOMPARE(textInput->text().length(), 0);
2170 simulateKeys(&canvas, QKeySequence::Paste);
2171 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2172 QCOMPARE(textInput->text().length(), 24);
2174 // clear copy buffer
2175 QClipboard *clipboard = QGuiApplication::clipboard();
2178 QVERIFY(!textInput->canPaste());
2180 // test that copy functionality is disabled
2181 // when echo mode is set to hide text/password mode
2184 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2185 textInput->setEchoMode(echoMode);
2186 textInput->setText("My password");
2187 textInput->select(0, textInput->text().length());;
2188 simulateKeys(&canvas, QKeySequence::Copy);
2189 if (echoMode == QQuickTextInput::Normal) {
2190 QVERIFY(!clipboard->text().isEmpty());
2191 QCOMPARE(clipboard->text(), QString("My password"));
2194 QVERIFY(clipboard->text().isEmpty());
2203 void tst_qquicktextinput::canPasteEmpty() {
2204 #ifndef QT_NO_CLIPBOARD
2206 QGuiApplication::clipboard()->clear();
2208 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2209 QDeclarativeComponent textInputComponent(&engine);
2210 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2211 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2212 QVERIFY(textInput != 0);
2214 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2215 QCOMPARE(textInput->canPaste(), cp);
2220 void tst_qquicktextinput::canPaste() {
2221 #ifndef QT_NO_CLIPBOARD
2223 QGuiApplication::clipboard()->setText("Some text");
2225 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2226 QDeclarativeComponent textInputComponent(&engine);
2227 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2228 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2229 QVERIFY(textInput != 0);
2231 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2232 QCOMPARE(textInput->canPaste(), cp);
2237 void tst_qquicktextinput::passwordCharacter()
2239 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2240 QDeclarativeComponent textInputComponent(&engine);
2241 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2242 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2243 QVERIFY(textInput != 0);
2245 textInput->setPasswordCharacter("X");
2246 qreal implicitWidth = textInput->implicitWidth();
2247 textInput->setPasswordCharacter(".");
2249 // QTBUG-12383 content is updated and redrawn
2250 QVERIFY(textInput->implicitWidth() < implicitWidth);
2255 void tst_qquicktextinput::cursorDelegate()
2257 QQuickView view(testFileUrl("cursorTest.qml"));
2259 view.requestActivateWindow();
2260 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2261 QVERIFY(textInputObject != 0);
2262 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2263 //Test Delegate gets created
2264 textInputObject->setFocus(true);
2265 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2266 QVERIFY(delegateObject);
2267 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2268 //Test Delegate gets moved
2269 for (int i=0; i<= textInputObject->text().length(); i++) {
2270 textInputObject->setCursorPosition(i);
2271 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2272 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2274 textInputObject->setCursorPosition(0);
2275 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2276 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2277 //Test Delegate gets deleted
2278 textInputObject->setCursorDelegate(0);
2279 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2282 void tst_qquicktextinput::cursorVisible()
2284 QQuickView view(testFileUrl("cursorVisible.qml"));
2286 view.requestActivateWindow();
2287 QTest::qWaitForWindowShown(&view);
2288 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2290 QQuickTextInput input;
2291 input.componentComplete();
2292 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2294 QCOMPARE(input.isCursorVisible(), false);
2296 input.setCursorVisible(true);
2297 QCOMPARE(input.isCursorVisible(), true);
2298 QCOMPARE(spy.count(), 1);
2300 input.setCursorVisible(false);
2301 QCOMPARE(input.isCursorVisible(), false);
2302 QCOMPARE(spy.count(), 2);
2304 input.setFocus(true);
2305 QCOMPARE(input.isCursorVisible(), false);
2306 QCOMPARE(spy.count(), 2);
2308 input.setParentItem(view.rootObject());
2309 QCOMPARE(input.isCursorVisible(), true);
2310 QCOMPARE(spy.count(), 3);
2312 input.setFocus(false);
2313 QCOMPARE(input.isCursorVisible(), false);
2314 QCOMPARE(spy.count(), 4);
2316 input.setFocus(true);
2317 QCOMPARE(input.isCursorVisible(), true);
2318 QCOMPARE(spy.count(), 5);
2320 QQuickView alternateView;
2321 alternateView.show();
2322 alternateView.requestActivateWindow();
2323 QTest::qWaitForWindowShown(&alternateView);
2325 QCOMPARE(input.isCursorVisible(), false);
2326 QCOMPARE(spy.count(), 6);
2328 view.requestActivateWindow();
2329 QTest::qWaitForWindowShown(&view);
2330 QCOMPARE(input.isCursorVisible(), true);
2331 QCOMPARE(spy.count(), 7);
2334 void tst_qquicktextinput::cursorRectangle()
2337 QString text = "Hello World!";
2339 QQuickTextInput input;
2340 input.setText(text);
2341 input.componentComplete();
2343 QTextLayout layout(text);
2344 layout.setFont(input.font());
2345 if (!qmlDisableDistanceField()) {
2347 option.setUseDesignMetrics(true);
2348 layout.setTextOption(option);
2350 layout.beginLayout();
2351 QTextLine line = layout.createLine();
2354 input.setWidth(line.cursorToX(5, QTextLine::Leading));
2355 input.setHeight(qCeil(line.height() * 3 / 2));
2359 // some tolerance for different fonts.
2361 const int error = 2;
2363 const int error = 5;
2366 for (int i = 0; i <= 5; ++i) {
2367 input.setCursorPosition(i);
2368 r = input.cursorRectangle();
2370 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2371 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2372 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2375 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2376 QVERIFY(r.left() < input.width());
2377 QVERIFY(r.right() >= input.width() - error);
2379 for (int i = 6; i < text.length(); ++i) {
2380 input.setCursorPosition(i);
2381 QCOMPARE(r, input.cursorRectangle());
2382 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2385 for (int i = text.length() - 2; i >= 0; --i) {
2386 input.setCursorPosition(i);
2387 r = input.cursorRectangle();
2388 QCOMPARE(r.top(), 0);
2389 QVERIFY(r.right() >= 0);
2390 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2393 // Check vertical scrolling with word wrap.
2394 input.setWrapMode(QQuickTextInput::WordWrap);
2395 for (int i = 0; i <= 5; ++i) {
2396 input.setCursorPosition(i);
2397 r = input.cursorRectangle();
2399 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2400 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2401 QCOMPARE(r.top(), 0);
2402 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2405 input.setCursorPosition(6);
2406 r = input.cursorRectangle();
2407 QCOMPARE(r.left(), 0);
2408 QVERIFY(r.bottom() >= input.height() - error);
2410 for (int i = 7; i < text.length(); ++i) {
2411 input.setCursorPosition(i);
2412 r = input.cursorRectangle();
2413 QVERIFY(r.bottom() >= input.height() - error);
2416 for (int i = text.length() - 2; i >= 6; --i) {
2417 input.setCursorPosition(i);
2418 r = input.cursorRectangle();
2419 QVERIFY(r.bottom() >= input.height() - error);
2422 for (int i = 5; i >= 0; --i) {
2423 input.setCursorPosition(i);
2424 r = input.cursorRectangle();
2425 QCOMPARE(r.top(), 0);
2428 input.setText("Hi!");
2429 input.setHAlign(QQuickTextInput::AlignRight);
2430 r = input.cursorRectangle();
2431 QVERIFY(r.left() < input.width() + error);
2432 QVERIFY(r.right() >= input.width() - error);
2435 void tst_qquicktextinput::readOnly()
2437 QQuickView canvas(testFileUrl("readOnly.qml"));
2439 canvas.requestActivateWindow();
2441 QVERIFY(canvas.rootObject() != 0);
2443 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2445 QVERIFY(input != 0);
2446 QTRY_VERIFY(input->hasActiveFocus() == true);
2447 QVERIFY(input->isReadOnly() == true);
2448 QString initial = input->text();
2449 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2450 simulateKey(&canvas, k);
2451 simulateKey(&canvas, Qt::Key_Return);
2452 simulateKey(&canvas, Qt::Key_Space);
2453 simulateKey(&canvas, Qt::Key_Escape);
2454 QCOMPARE(input->text(), initial);
2456 input->setCursorPosition(3);
2457 input->setReadOnly(false);
2458 QCOMPARE(input->isReadOnly(), false);
2459 QCOMPARE(input->cursorPosition(), input->text().length());
2462 void tst_qquicktextinput::echoMode()
2464 QQuickView canvas(testFileUrl("echoMode.qml"));
2466 canvas.requestActivateWindow();
2467 QTest::qWaitForWindowShown(&canvas);
2468 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2470 QVERIFY(canvas.rootObject() != 0);
2472 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2474 QVERIFY(input != 0);
2475 QTRY_VERIFY(input->hasActiveFocus() == true);
2476 QString initial = input->text();
2477 Qt::InputMethodHints ref;
2478 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2479 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2480 QCOMPARE(input->displayText(), input->text());
2482 ref &= ~Qt::ImhHiddenText;
2483 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2484 QCOMPARE(input->inputMethodHints(), ref);
2485 input->setEchoMode(QQuickTextInput::NoEcho);
2486 QCOMPARE(input->text(), initial);
2487 QCOMPARE(input->displayText(), QLatin1String(""));
2488 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2490 ref |= Qt::ImhHiddenText;
2491 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2492 QCOMPARE(input->inputMethodHints(), ref);
2493 input->setEchoMode(QQuickTextInput::Password);
2495 ref |= Qt::ImhHiddenText;
2496 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2497 QCOMPARE(input->text(), initial);
2498 QCOMPARE(input->displayText(), QLatin1String("********"));
2499 QCOMPARE(input->inputMethodHints(), ref);
2500 input->setPasswordCharacter(QChar('Q'));
2501 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2502 QCOMPARE(input->text(), initial);
2503 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2504 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2505 //PasswordEchoOnEdit
2506 ref &= ~Qt::ImhHiddenText;
2507 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2508 QCOMPARE(input->inputMethodHints(), ref);
2509 QCOMPARE(input->text(), initial);
2510 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2511 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2512 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2513 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2514 QCOMPARE(input->text(), QLatin1String("a"));
2515 QCOMPARE(input->displayText(), QLatin1String("a"));
2516 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2517 input->setFocus(false);
2518 QVERIFY(input->hasActiveFocus() == false);
2519 QCOMPARE(input->displayText(), QLatin1String("Q"));
2520 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2521 input->setFocus(true);
2522 QVERIFY(input->hasActiveFocus());
2523 QInputMethodEvent inputEvent;
2524 inputEvent.setCommitString(initial);
2525 QGuiApplication::sendEvent(input, &inputEvent);
2526 QCOMPARE(input->text(), initial);
2527 QCOMPARE(input->displayText(), initial);
2528 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2531 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2532 void tst_qquicktextinput::passwordEchoDelay()
2534 QQuickView canvas(testFileUrl("echoMode.qml"));
2536 canvas.requestActivateWindow();
2537 QTest::qWaitForWindowShown(&canvas);
2538 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2540 QVERIFY(canvas.rootObject() != 0);
2542 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2544 QChar fillChar = QLatin1Char('*');
2546 input->setEchoMode(QQuickTextInput::Password);
2547 QCOMPARE(input->displayText(), QString(8, fillChar));
2548 input->setText(QString());
2549 QCOMPARE(input->displayText(), QString());
2551 QTest::keyPress(&canvas, '0');
2552 QTest::keyPress(&canvas, '1');
2553 QTest::keyPress(&canvas, '2');
2554 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2555 QTest::keyPress(&canvas, '3');
2556 QTest::keyPress(&canvas, '4');
2557 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2558 QTest::keyPress(&canvas, Qt::Key_Backspace);
2559 QCOMPARE(input->displayText(), QString(4, fillChar));
2560 QTest::keyPress(&canvas, '4');
2561 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2562 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2563 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2564 QTest::keyPress(&canvas, '5');
2565 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2566 input->setFocus(false);
2567 QVERIFY(!input->hasFocus());
2568 QCOMPARE(input->displayText(), QString(6, fillChar));
2569 input->setFocus(true);
2570 QTRY_VERIFY(input->hasFocus());
2571 QCOMPARE(input->displayText(), QString(6, fillChar));
2572 QTest::keyPress(&canvas, '6');
2573 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2575 QInputMethodEvent ev;
2576 ev.setCommitString(QLatin1String("7"));
2577 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2578 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2580 input->setCursorPosition(3);
2581 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2582 QTest::keyPress(&canvas, 'a');
2583 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2584 QTest::keyPress(&canvas, Qt::Key_Backspace);
2585 QCOMPARE(input->displayText(), QString(8, fillChar));
2590 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2592 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2593 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2595 QGuiApplication::sendEvent(view, &press);
2596 QGuiApplication::sendEvent(view, &release);
2600 void tst_qquicktextinput::openInputPanel()
2602 PlatformInputContext platformInputContext;
2603 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2604 inputPanelPrivate->testContext = &platformInputContext;
2606 QQuickView view(testFileUrl("openInputPanel.qml"));
2608 view.requestActivateWindow();
2609 QTest::qWaitForWindowShown(&view);
2610 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2612 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2615 // check default values
2616 QVERIFY(input->focusOnPress());
2617 QVERIFY(!input->hasActiveFocus());
2618 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2619 QCOMPARE(qApp->inputPanel()->visible(), false);
2621 // input panel should open on focus
2622 QPoint centerPoint(view.width()/2, view.height()/2);
2623 Qt::KeyboardModifiers noModifiers = 0;
2624 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2625 QGuiApplication::processEvents();
2626 QVERIFY(input->hasActiveFocus());
2627 QCOMPARE(qApp->inputPanel()->inputItem(), input);
2628 QCOMPARE(qApp->inputPanel()->visible(), true);
2629 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2631 // input panel should be re-opened when pressing already focused TextInput
2632 qApp->inputPanel()->hide();
2633 QCOMPARE(qApp->inputPanel()->visible(), false);
2634 QVERIFY(input->hasActiveFocus());
2635 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2636 QGuiApplication::processEvents();
2637 QCOMPARE(qApp->inputPanel()->visible(), true);
2638 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2640 // input panel should stay visible if focus is lost to another text inputor
2641 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2642 QQuickTextInput anotherInput;
2643 anotherInput.componentComplete();
2644 anotherInput.setParentItem(view.rootObject());
2645 anotherInput.setFocus(true);
2646 QCOMPARE(qApp->inputPanel()->visible(), true);
2647 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2648 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2650 anotherInput.setFocus(false);
2651 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2652 QCOMPARE(view.activeFocusItem(), view.rootItem());
2653 anotherInput.setFocus(true);
2655 // input item should be null if focus is lost to an item that doesn't accept inputs
2657 item.setParentItem(view.rootObject());
2658 item.setFocus(true);
2659 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2660 QCOMPARE(view.activeFocusItem(), &item);
2662 qApp->inputPanel()->hide();
2664 // input panel should not be opened if TextInput is read only
2665 input->setReadOnly(true);
2666 input->setFocus(true);
2667 QCOMPARE(qApp->inputPanel()->visible(), false);
2668 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2669 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2670 QGuiApplication::processEvents();
2671 QCOMPARE(qApp->inputPanel()->visible(), false);
2673 // input panel should not be opened if focusOnPress is set to false
2674 input->setFocusOnPress(false);
2675 input->setFocus(false);
2676 input->setFocus(true);
2677 QCOMPARE(qApp->inputPanel()->visible(), false);
2678 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2679 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2680 QCOMPARE(qApp->inputPanel()->visible(), false);
2682 // input panel should open when openSoftwareInputPanel is called
2683 input->openSoftwareInputPanel();
2684 QCOMPARE(qApp->inputPanel()->visible(), true);
2686 // input panel should close when closeSoftwareInputPanel is called
2687 input->closeSoftwareInputPanel();
2688 QCOMPARE(qApp->inputPanel()->visible(), false);
2691 class MyTextInput : public QQuickTextInput
2694 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2698 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2701 return QQuickTextInput::updatePaintNode(node, data);
2706 void tst_qquicktextinput::setHAlignClearCache()
2710 input.setText("Hello world");
2711 input.setParentItem(view.rootItem());
2713 view.requestActivateWindow();
2714 QTest::qWaitForWindowShown(&view);
2716 QEXPECT_FAIL("", "QTBUG-23485", Abort);
2718 QTRY_COMPARE(input.nbPaint, 1);
2719 input.setHAlign(QQuickTextInput::AlignRight);
2720 //Changing the alignment should trigger a repaint
2721 QTRY_COMPARE(input.nbPaint, 2);
2724 void tst_qquicktextinput::focusOutClearSelection()
2727 QQuickTextInput input;
2728 QQuickTextInput input2;
2729 input.setText(QLatin1String("Hello world"));
2730 input.setFocus(true);
2731 input2.setParentItem(view.rootItem());
2732 input.setParentItem(view.rootItem());
2733 input.componentComplete();
2734 input2.componentComplete();
2736 view.requestActivateWindow();
2737 QTest::qWaitForWindowShown(&view);
2739 //The selection should work
2740 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2741 input2.setFocus(true);
2742 QGuiApplication::processEvents();
2743 //The input lost the focus selection should be cleared
2744 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2747 void tst_qquicktextinput::geometrySignals()
2749 QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2750 QObject *o = component.create();
2752 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2753 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2757 void tst_qquicktextinput::testQtQuick11Attributes()
2759 QFETCH(QString, code);
2760 QFETCH(QString, warning);
2761 QFETCH(QString, error);
2763 QDeclarativeEngine engine;
2766 QDeclarativeComponent valid(&engine);
2767 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2768 obj = valid.create();
2770 QVERIFY(valid.errorString().isEmpty());
2773 QDeclarativeComponent invalid(&engine);
2774 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2775 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2776 obj = invalid.create();
2777 QCOMPARE(invalid.errorString(), error);
2781 void tst_qquicktextinput::testQtQuick11Attributes_data()
2783 QTest::addColumn<QString>("code");
2784 QTest::addColumn<QString>("warning");
2785 QTest::addColumn<QString>("error");
2787 QTest::newRow("canPaste") << "property bool foo: canPaste"
2788 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2791 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2792 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2795 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2796 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2800 static void sendPreeditText(const QString &text, int cursor)
2802 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2803 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2804 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2807 void tst_qquicktextinput::preeditAutoScroll()
2809 QString preeditText = "califragisiticexpialidocious!";
2811 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2813 view.requestActivateWindow();
2814 QTest::qWaitForWindowShown(&view);
2815 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2816 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2818 QVERIFY(input->hasActiveFocus());
2820 input->setWidth(input->implicitWidth());
2822 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2823 int cursorRectangleChanges = 0;
2825 // test the text is scrolled so the preedit is visible.
2826 sendPreeditText(preeditText.mid(0, 3), 1);
2827 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2828 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2829 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2831 // test the text is scrolled back when the preedit is removed.
2832 QInputMethodEvent imEvent;
2833 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2834 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2835 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2836 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2838 QTextLayout layout(preeditText);
2839 layout.setFont(input->font());
2840 if (!qmlDisableDistanceField()) {
2842 option.setUseDesignMetrics(true);
2843 layout.setTextOption(option);
2845 layout.beginLayout();
2846 QTextLine line = layout.createLine();
2849 // test if the preedit is larger than the text input that the
2850 // character preceding the cursor is still visible.
2851 qreal x = input->positionToRectangle(0).x();
2852 for (int i = 0; i < 3; ++i) {
2853 sendPreeditText(preeditText, i + 1);
2854 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2855 QVERIFY(input->cursorRectangle().right() >= width - 3);
2856 QVERIFY(input->positionToRectangle(0).x() < x);
2857 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2858 x = input->positionToRectangle(0).x();
2860 for (int i = 1; i >= 0; --i) {
2861 sendPreeditText(preeditText, i + 1);
2862 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2863 QVERIFY(input->cursorRectangle().right() >= width - 3);
2864 QVERIFY(input->positionToRectangle(0).x() > x);
2865 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2866 x = input->positionToRectangle(0).x();
2869 // Test incrementing the preedit cursor doesn't cause further
2870 // scrolling when right most text is visible.
2871 sendPreeditText(preeditText, preeditText.length() - 3);
2872 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2873 x = input->positionToRectangle(0).x();
2874 for (int i = 2; i >= 0; --i) {
2875 sendPreeditText(preeditText, preeditText.length() - i);
2876 QCOMPARE(input->positionToRectangle(0).x(), x);
2877 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2879 for (int i = 1; i < 3; ++i) {
2880 sendPreeditText(preeditText, preeditText.length() - i);
2881 QCOMPARE(input->positionToRectangle(0).x(), x);
2882 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2885 // Test disabling auto scroll.
2886 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2888 input->setAutoScroll(false);
2889 sendPreeditText(preeditText.mid(0, 3), 1);
2890 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2891 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2894 void tst_qquicktextinput::preeditCursorRectangle()
2896 QString preeditText = "super";
2898 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2900 view.requestActivateWindow();
2901 QTest::qWaitForWindowShown(&view);
2902 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2903 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2908 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2909 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2910 QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2912 // Verify that the micro focus rect is positioned the same for position 0 as
2913 // it would be if there was no preedit text.
2914 sendPreeditText(preeditText, 0);
2915 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2916 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2917 QCOMPARE(currentRect, previousRect);
2919 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2920 QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2922 // Verify that the micro focus rect moves to the left as the cursor position
2924 for (int i = 1; i <= 5; ++i) {
2925 sendPreeditText(preeditText, i);
2926 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2927 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2928 QVERIFY(previousRect.left() < currentRect.left());
2929 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2930 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2931 previousRect = currentRect;
2934 // Verify that if there is no preedit cursor then the micro focus rect is the
2935 // same as it would be if it were positioned at the end of the preedit text.
2936 sendPreeditText(preeditText, 0);
2937 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2938 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2939 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2940 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2941 QCOMPARE(currentRect, previousRect);
2942 QVERIFY(inputSpy.count() > 0);
2943 QVERIFY(panelSpy.count() > 0);
2946 void tst_qquicktextinput::inputContextMouseHandler()
2948 PlatformInputContext platformInputContext;
2949 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2950 inputPanelPrivate->testContext = &platformInputContext;
2952 QString text = "supercalifragisiticexpialidocious!";
2953 QQuickView view(testFileUrl("inputContext.qml"));
2954 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2957 input->setFocus(true);
2961 view.requestActivateWindow();
2962 QTest::qWaitForWindowShown(&view);
2963 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2965 QTextLayout layout(text);
2966 layout.setFont(input->font());
2967 if (!qmlDisableDistanceField()) {
2969 option.setUseDesignMetrics(true);
2970 layout.setTextOption(option);
2972 layout.beginLayout();
2973 QTextLine line = layout.createLine();
2976 const qreal x = line.cursorToX(2, QTextLine::Leading);
2977 const qreal y = line.height() / 2;
2978 QPoint position = QPointF(x, y).toPoint();
2980 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2981 QGuiApplication::sendEvent(input, &inputEvent);
2983 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2984 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2985 QGuiApplication::processEvents();
2987 QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2988 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2989 QCOMPARE(platformInputContext.m_cursorPosition, 2);
2992 void tst_qquicktextinput::inputMethodComposing()
2994 QString text = "supercalifragisiticexpialidocious!";
2996 QQuickView view(testFileUrl("inputContext.qml"));
2998 view.requestActivateWindow();
2999 QTest::qWaitForWindowShown(&view);
3000 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3001 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3003 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3005 QCOMPARE(input->isInputMethodComposing(), false);
3007 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3008 QGuiApplication::sendEvent(input, &event);
3010 QCOMPARE(input->isInputMethodComposing(), true);
3011 QCOMPARE(spy.count(), 1);
3014 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3015 QGuiApplication::sendEvent(input, &event);
3017 QCOMPARE(spy.count(), 1);
3020 QInputMethodEvent event;
3021 QGuiApplication::sendEvent(input, &event);
3023 QCOMPARE(input->isInputMethodComposing(), false);
3024 QCOMPARE(spy.count(), 2);
3027 void tst_qquicktextinput::inputPanelUpdate()
3029 PlatformInputContext platformInputContext;
3030 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
3031 inputPanelPrivate->testContext = &platformInputContext;
3033 QQuickView view(testFileUrl("inputContext.qml"));
3035 view.requestActivateWindow();
3036 QTest::qWaitForWindowShown(&view);
3037 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3038 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3041 // text change even without cursor position change needs to trigger update
3042 input->setText("test");
3043 platformInputContext.clear();
3044 input->setText("xxxx");
3045 QVERIFY(platformInputContext.m_updateCallCount > 0);
3047 // input method event replacing text
3048 platformInputContext.clear();
3050 QInputMethodEvent inputMethodEvent;
3051 inputMethodEvent.setCommitString("y", -1, 1);
3052 QGuiApplication::sendEvent(input, &inputMethodEvent);
3054 QVERIFY(platformInputContext.m_updateCallCount > 0);
3056 // input method changing selection
3057 platformInputContext.clear();
3059 QList<QInputMethodEvent::Attribute> attributes;
3060 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3061 QInputMethodEvent inputMethodEvent("", attributes);
3062 QGuiApplication::sendEvent(input, &inputMethodEvent);
3064 QVERIFY(input->selectionStart() != input->selectionEnd());
3065 QVERIFY(platformInputContext.m_updateCallCount > 0);
3067 // programmatical selections trigger update
3068 platformInputContext.clear();
3070 QVERIFY(platformInputContext.m_updateCallCount > 0);
3073 platformInputContext.clear();
3074 QFont font = input->font();
3075 font.setBold(!font.bold());
3076 input->setFont(font);
3077 QVERIFY(platformInputContext.m_updateCallCount > 0);
3080 platformInputContext.clear();
3082 QInputMethodEvent inputMethodEvent;
3083 inputMethodEvent.setCommitString("y");
3084 QGuiApplication::sendEvent(input, &inputMethodEvent);
3086 QVERIFY(platformInputContext.m_updateCallCount > 0);
3088 // changing cursor position
3089 platformInputContext.clear();
3090 input->setCursorPosition(0);
3091 QVERIFY(platformInputContext.m_updateCallCount > 0);
3093 // read only disabled input method
3094 platformInputContext.clear();
3095 input->setReadOnly(true);
3096 QVERIFY(platformInputContext.m_updateCallCount > 0);
3097 input->setReadOnly(false);
3099 // no updates while no focus
3100 input->setFocus(false);
3101 platformInputContext.clear();
3102 input->setText("Foo");
3103 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3104 input->setCursorPosition(1);
3105 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3107 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3108 input->setReadOnly(true);
3109 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3112 void tst_qquicktextinput::cursorRectangleSize()
3114 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3115 QVERIFY(canvas->rootObject() != 0);
3116 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3118 // make sure cursor rectangle is not at (0,0)
3119 textInput->setX(10);
3120 textInput->setY(10);
3121 textInput->setCursorPosition(3);
3122 QVERIFY(textInput != 0);
3123 textInput->setFocus(true);
3125 canvas->requestActivateWindow();
3126 QTest::qWaitForWindowShown(canvas);
3128 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3129 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
3130 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3132 QRect cursorRectFromItem = textInput->cursorRectangle();
3133 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3135 // item and input query cursor rectangles match
3136 QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
3138 // item cursor rectangle and positionToRectangle calculations match
3139 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
3141 // item-canvas transform and input item transform match
3142 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
3144 // input panel cursorRectangle property and tranformed item cursor rectangle match
3145 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3146 QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
3151 void tst_qquicktextinput::tripleClickSelectsAll()
3153 QString qmlfile = testFile("positionAt.qml");
3154 QQuickView view(QUrl::fromLocalFile(qmlfile));
3156 view.requestActivateWindow();
3157 QTest::qWaitForWindowShown(&view);
3159 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3161 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3164 QLatin1String hello("Hello world!");
3165 input->setSelectByMouse(true);
3166 input->setText(hello);
3168 // Clicking on the same point inside TextInput three times in a row
3169 // should trigger a triple click, thus selecting all the text.
3170 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3171 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3172 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3173 QGuiApplication::processEvents();
3174 QCOMPARE(input->selectedText(), hello);
3176 // Now it simulates user moving the mouse between the second and the third click.
3177 // In this situation, we don't expect a triple click.
3178 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3179 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3180 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3181 QGuiApplication::processEvents();
3182 QVERIFY(input->selectedText().isEmpty());
3184 // And now we press the third click too late, so no triple click event is triggered.
3185 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3186 QGuiApplication::processEvents();
3187 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3188 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3189 QGuiApplication::processEvents();
3190 QVERIFY(input->selectedText().isEmpty());
3193 void tst_qquicktextinput::QTBUG_19956_data()
3195 QTest::addColumn<QString>("url");
3196 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3197 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3201 void tst_qquicktextinput::getText_data()
3203 QTest::addColumn<QString>("text");
3204 QTest::addColumn<QString>("inputMask");
3205 QTest::addColumn<int>("start");
3206 QTest::addColumn<int>("end");
3207 QTest::addColumn<QString>("expectedText");
3209 QTest::newRow("all plain text")
3212 << 0 << standard.at(0).length()
3215 QTest::newRow("plain text sub string")
3219 << standard.at(0).mid(0, 12);
3221 QTest::newRow("plain text sub string reversed")
3225 << standard.at(0).mid(0, 12);
3227 QTest::newRow("plain text cropped beginning")
3231 << standard.at(0).mid(0, 4);
3233 QTest::newRow("plain text cropped end")
3236 << 23 << standard.at(0).length() + 8
3237 << standard.at(0).mid(23);
3239 QTest::newRow("plain text cropped beginning and end")
3242 << -9 << standard.at(0).length() + 4
3246 void tst_qquicktextinput::getText()
3248 QFETCH(QString, text);
3249 QFETCH(QString, inputMask);
3252 QFETCH(QString, expectedText);
3254 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3255 QDeclarativeComponent textInputComponent(&engine);
3256 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3257 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3258 QVERIFY(textInput != 0);
3260 QCOMPARE(textInput->getText(start, end), expectedText);
3263 void tst_qquicktextinput::insert_data()
3265 QTest::addColumn<QString>("text");
3266 QTest::addColumn<QString>("inputMask");
3267 QTest::addColumn<int>("selectionStart");
3268 QTest::addColumn<int>("selectionEnd");
3269 QTest::addColumn<int>("insertPosition");
3270 QTest::addColumn<QString>("insertText");
3271 QTest::addColumn<QString>("expectedText");
3272 QTest::addColumn<int>("expectedSelectionStart");
3273 QTest::addColumn<int>("expectedSelectionEnd");
3274 QTest::addColumn<int>("expectedCursorPosition");
3275 QTest::addColumn<bool>("selectionChanged");
3276 QTest::addColumn<bool>("cursorPositionChanged");
3278 QTest::newRow("at cursor position (beginning)")
3283 << QString("Hello") + standard.at(0)
3287 QTest::newRow("at cursor position (end)")
3290 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3292 << standard.at(0) + QString("Hello")
3293 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3296 QTest::newRow("at cursor position (middle)")
3301 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3305 QTest::newRow("after cursor position (beginning)")
3310 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3314 QTest::newRow("before cursor position (end)")
3317 << standard.at(0).length() << standard.at(0).length() << 18
3319 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3320 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3323 QTest::newRow("before cursor position (middle)")
3328 << QString("Hello") + standard.at(0)
3332 QTest::newRow("after cursor position (middle)")
3335 << 18 << 18 << standard.at(0).length()
3337 << standard.at(0) + QString("Hello")
3341 QTest::newRow("before selection")
3346 << QString("Hello") + standard.at(0)
3350 QTest::newRow("before reversed selection")
3355 << QString("Hello") + standard.at(0)
3359 QTest::newRow("after selection")
3362 << 14 << 19 << standard.at(0).length()
3364 << standard.at(0) + QString("Hello")
3368 QTest::newRow("after reversed selection")
3371 << 19 << 14 << standard.at(0).length()
3373 << standard.at(0) + QString("Hello")
3377 QTest::newRow("into selection")
3382 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3386 QTest::newRow("into reversed selection")
3391 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3395 QTest::newRow("rich text into plain text")
3399 << QString("<b>Hello</b>")
3400 << QString("<b>Hello</b>") + standard.at(0)
3404 QTest::newRow("before start")
3413 QTest::newRow("past end")
3416 << 0 << 0 << standard.at(0).length() + 3
3422 const QString inputMask = "009.009.009.009";
3423 const QString ip = "192.168.2.14";
3425 QTest::newRow("mask: at cursor position (beginning)")
3430 << QString("125.168.2.14")
3434 QTest::newRow("mask: at cursor position (end)")
3437 << inputMask.length() << inputMask.length() << inputMask.length()
3440 << inputMask.length() << inputMask.length() << inputMask.length()
3443 QTest::newRow("mask: at cursor position (middle)")
3448 << QString("192.167.5.24")
3452 QTest::newRow("mask: after cursor position (beginning)")
3457 << QString("192.167.5.24")
3461 QTest::newRow("mask: before cursor position (end)")
3464 << inputMask.length() << inputMask.length() << 6
3466 << QString("192.167.5.24")
3467 << inputMask.length() << inputMask.length() << inputMask.length()
3470 QTest::newRow("mask: before cursor position (middle)")
3475 << QString("125.168.2.14")
3479 QTest::newRow("mask: after cursor position (middle)")
3488 QTest::newRow("mask: before selection")
3493 << QString("125.168.2.14")
3497 QTest::newRow("mask: before reversed selection")
3502 << QString("125.168.2.14")
3506 QTest::newRow("mask: after selection")
3515 QTest::newRow("mask: after reversed selection")
3524 QTest::newRow("mask: into selection")
3529 << QString("192.167.5.24")
3533 QTest::newRow("mask: into reversed selection")
3538 << QString("192.167.5.24")
3542 QTest::newRow("mask: before start")
3551 QTest::newRow("mask: past end")
3554 << 0 << 0 << ip.length() + 3
3560 QTest::newRow("mask: invalid characters")
3565 << QString("192.168.2.14")
3569 QTest::newRow("mask: mixed validity")
3573 << QString("a1b2c5")
3574 << QString("125.168.2.14")
3579 void tst_qquicktextinput::insert()
3581 QFETCH(QString, text);
3582 QFETCH(QString, inputMask);
3583 QFETCH(int, selectionStart);
3584 QFETCH(int, selectionEnd);
3585 QFETCH(int, insertPosition);
3586 QFETCH(QString, insertText);
3587 QFETCH(QString, expectedText);
3588 QFETCH(int, expectedSelectionStart);
3589 QFETCH(int, expectedSelectionEnd);
3590 QFETCH(int, expectedCursorPosition);
3591 QFETCH(bool, selectionChanged);
3592 QFETCH(bool, cursorPositionChanged);
3594 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3595 QDeclarativeComponent textInputComponent(&engine);
3596 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3597 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3598 QVERIFY(textInput != 0);
3600 textInput->select(selectionStart, selectionEnd);
3602 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3603 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3604 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3605 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3606 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3608 textInput->insert(insertPosition, insertText);
3610 QCOMPARE(textInput->text(), expectedText);
3611 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3613 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3614 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3615 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3617 if (selectionStart > selectionEnd)
3618 qSwap(selectionStart, selectionEnd);
3620 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3621 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3622 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3623 QCOMPARE(textSpy.count() > 0, text != expectedText);
3624 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3627 void tst_qquicktextinput::remove_data()
3629 QTest::addColumn<QString>("text");
3630 QTest::addColumn<QString>("inputMask");
3631 QTest::addColumn<int>("selectionStart");
3632 QTest::addColumn<int>("selectionEnd");
3633 QTest::addColumn<int>("removeStart");
3634 QTest::addColumn<int>("removeEnd");
3635 QTest::addColumn<QString>("expectedText");
3636 QTest::addColumn<int>("expectedSelectionStart");
3637 QTest::addColumn<int>("expectedSelectionEnd");
3638 QTest::addColumn<int>("expectedCursorPosition");
3639 QTest::addColumn<bool>("selectionChanged");
3640 QTest::addColumn<bool>("cursorPositionChanged");
3642 QTest::newRow("from cursor position (beginning)")
3647 << standard.at(0).mid(5)
3651 QTest::newRow("to cursor position (beginning)")
3656 << standard.at(0).mid(5)
3660 QTest::newRow("to cursor position (end)")
3663 << standard.at(0).length() << standard.at(0).length()
3664 << standard.at(0).length() << standard.at(0).length() - 5
3665 << standard.at(0).mid(0, standard.at(0).length() - 5)
3666 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3669 QTest::newRow("to cursor position (end)")
3672 << standard.at(0).length() << standard.at(0).length()
3673 << standard.at(0).length() - 5 << standard.at(0).length()
3674 << standard.at(0).mid(0, standard.at(0).length() - 5)
3675 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3678 QTest::newRow("from cursor position (middle)")
3683 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3687 QTest::newRow("to cursor position (middle)")
3692 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3696 QTest::newRow("after cursor position (beginning)")
3701 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3705 QTest::newRow("before cursor position (end)")
3708 << standard.at(0).length() << standard.at(0).length()
3710 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3711 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3714 QTest::newRow("before cursor position (middle)")
3719 << standard.at(0).mid(5)
3723 QTest::newRow("after cursor position (middle)")
3728 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3732 QTest::newRow("before selection")
3737 << standard.at(0).mid(5)
3741 QTest::newRow("before reversed selection")
3746 << standard.at(0).mid(5)
3750 QTest::newRow("after selection")
3754 << standard.at(0).length() - 5 << standard.at(0).length()
3755 << standard.at(0).mid(0, standard.at(0).length() - 5)
3759 QTest::newRow("after reversed selection")
3763 << standard.at(0).length() - 5 << standard.at(0).length()
3764 << standard.at(0).mid(0, standard.at(0).length() - 5)
3768 QTest::newRow("from selection")
3773 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3777 QTest::newRow("from reversed selection")
3782 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3786 QTest::newRow("cropped beginning")
3791 << standard.at(0).mid(4)
3795 QTest::newRow("cropped end")
3799 << 23 << standard.at(0).length() + 8
3800 << standard.at(0).mid(0, 23)
3804 QTest::newRow("cropped beginning and end")
3808 << -9 << standard.at(0).length() + 4
3813 const QString inputMask = "009.009.009.009";
3814 const QString ip = "192.168.2.14";
3816 QTest::newRow("mask: from cursor position")
3821 << QString("192.16..14")
3825 QTest::newRow("mask: to cursor position")
3830 << QString("19.8.2.14")
3834 QTest::newRow("mask: before cursor position")
3839 << QString("2.168.2.14")
3843 QTest::newRow("mask: after cursor position")
3848 << QString("192.168.2.")
3852 QTest::newRow("mask: before selection")
3857 << QString("2.168.2.14")
3861 QTest::newRow("mask: before reversed selection")
3866 << QString("2.168.2.14")
3870 QTest::newRow("mask: after selection")
3875 << QString("192.168.2.")
3879 QTest::newRow("mask: after reversed selection")
3884 << QString("192.168.2.")
3888 QTest::newRow("mask: from selection")
3893 << QString("192.168..14")
3897 QTest::newRow("mask: from reversed selection")
3902 << QString("192.168..14")
3906 QTest::newRow("mask: cropped beginning")
3911 << QString(".168.2.14")
3915 QTest::newRow("mask: cropped end")
3920 << QString("192.168.2.1")
3924 QTest::newRow("mask: cropped beginning and end")
3934 void tst_qquicktextinput::remove()
3936 QFETCH(QString, text);
3937 QFETCH(QString, inputMask);
3938 QFETCH(int, selectionStart);
3939 QFETCH(int, selectionEnd);
3940 QFETCH(int, removeStart);
3941 QFETCH(int, removeEnd);
3942 QFETCH(QString, expectedText);
3943 QFETCH(int, expectedSelectionStart);
3944 QFETCH(int, expectedSelectionEnd);
3945 QFETCH(int, expectedCursorPosition);
3946 QFETCH(bool, selectionChanged);
3947 QFETCH(bool, cursorPositionChanged);
3949 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3950 QDeclarativeComponent textInputComponent(&engine);
3951 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3952 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3953 QVERIFY(textInput != 0);
3955 textInput->select(selectionStart, selectionEnd);
3957 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3958 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3959 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3960 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3961 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3963 textInput->remove(removeStart, removeEnd);
3965 QCOMPARE(textInput->text(), expectedText);
3966 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3968 if (selectionStart > selectionEnd) //
3969 qSwap(selectionStart, selectionEnd);
3971 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3972 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3973 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3975 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3976 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3977 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3978 QCOMPARE(textSpy.count() > 0, text != expectedText);
3980 if (cursorPositionChanged) //
3981 QVERIFY(cursorPositionSpy.count() > 0);
3984 void tst_qquicktextinput::keySequence_data()
3986 QTest::addColumn<QString>("text");
3987 QTest::addColumn<QKeySequence>("sequence");
3988 QTest::addColumn<int>("selectionStart");
3989 QTest::addColumn<int>("selectionEnd");
3990 QTest::addColumn<int>("cursorPosition");
3991 QTest::addColumn<QString>("expectedText");
3992 QTest::addColumn<QString>("selectedText");
3994 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3996 QTest::newRow("select all")
3997 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3998 << 44 << standard.at(0) << standard.at(0);
3999 QTest::newRow("select end of line")
4000 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4001 << 44 << standard.at(0) << standard.at(0).mid(5);
4002 QTest::newRow("select end of document") // ### Not handled.
4003 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4004 << 3 << standard.at(0) << QString();
4005 QTest::newRow("select end of block")
4006 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4007 << 44 << standard.at(0) << standard.at(0).mid(18);
4008 QTest::newRow("delete end of line")
4009 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4010 << 24 << standard.at(0).mid(0, 24) << QString();
4011 QTest::newRow("move to start of line")
4012 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4013 << 0 << standard.at(0) << QString();
4014 QTest::newRow("move to start of block")
4015 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4016 << 0 << standard.at(0) << QString();
4017 QTest::newRow("move to next char")
4018 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4019 << 13 << standard.at(0) << QString();
4020 QTest::newRow("move to previous char")
4021 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4022 << 2 << standard.at(0) << QString();
4023 QTest::newRow("select next char")
4024 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4025 << 24 << standard.at(0) << standard.at(0).mid(23, 1);
4026 QTest::newRow("select previous char")
4027 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4028 << 18 << standard.at(0) << standard.at(0).mid(18, 1);
4029 QTest::newRow("move to next word")
4030 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4031 << 10 << standard.at(0) << QString();
4032 QTest::newRow("move to previous word")
4033 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4034 << 4 << standard.at(0) << QString();
4035 QTest::newRow("select previous word")
4036 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4037 << 10 << standard.at(0) << standard.at(0).mid(10, 1);
4038 QTest::newRow("delete (selection)")
4039 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4040 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
4041 QTest::newRow("delete (no selection)")
4042 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4043 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
4044 QTest::newRow("delete end of word")
4045 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4046 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
4047 QTest::newRow("delete start of word")
4048 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4049 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
4052 void tst_qquicktextinput::keySequence()
4054 QFETCH(QString, text);
4055 QFETCH(QKeySequence, sequence);
4056 QFETCH(int, selectionStart);
4057 QFETCH(int, selectionEnd);
4058 QFETCH(int, cursorPosition);
4059 QFETCH(QString, expectedText);
4060 QFETCH(QString, selectedText);
4062 if (sequence.isEmpty()) {
4063 QSKIP("Key sequence is undefined");
4066 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4067 QDeclarativeComponent textInputComponent(&engine);
4068 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4069 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4070 QVERIFY(textInput != 0);
4072 QQuickCanvas canvas;
4073 textInput->setParentItem(canvas.rootItem());
4075 canvas.requestActivateWindow();
4076 QTest::qWaitForWindowShown(&canvas);
4077 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4079 textInput->select(selectionStart, selectionEnd);
4081 simulateKeys(&canvas, sequence);
4083 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4084 QCOMPARE(textInput->text(), expectedText);
4085 QCOMPARE(textInput->selectedText(), selectedText);
4089 #define REPLACE_UNTIL_END 1
4091 void tst_qquicktextinput::undo_data()
4093 QTest::addColumn<QStringList>("insertString");
4094 QTest::addColumn<IntList>("insertIndex");
4095 QTest::addColumn<IntList>("insertMode");
4096 QTest::addColumn<QStringList>("expectedString");
4097 QTest::addColumn<bool>("use_keys");
4099 for (int i=0; i<2; i++) {
4100 QString keys_str = "keyboard";
4101 bool use_keys = true;
4103 keys_str = "insert";
4108 IntList insertIndex;
4110 QStringList insertString;
4111 QStringList expectedString;
4114 insertMode << NORMAL;
4115 insertString << "1";
4118 insertMode << NORMAL;
4119 insertString << "5";
4122 insertMode << NORMAL;
4123 insertString << "3";
4126 insertMode << NORMAL;
4127 insertString << "2";
4130 insertMode << NORMAL;
4131 insertString << "4";
4133 expectedString << "12345";
4134 expectedString << "1235";
4135 expectedString << "135";
4136 expectedString << "15";
4137 expectedString << "";
4139 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4147 IntList insertIndex;
4149 QStringList insertString;
4150 QStringList expectedString;
4153 insertMode << NORMAL;
4154 insertString << "World"; // World
4157 insertMode << NORMAL;
4158 insertString << "Hello"; // HelloWorld
4161 insertMode << NORMAL;
4162 insertString << "Well"; // WellHelloWorld
4165 insertMode << NORMAL;
4166 insertString << "There"; // WellHelloThereWorld;
4168 expectedString << "WellHelloThereWorld";
4169 expectedString << "WellHelloWorld";
4170 expectedString << "HelloWorld";
4171 expectedString << "World";
4172 expectedString << "";
4174 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4182 IntList insertIndex;
4184 QStringList insertString;
4185 QStringList expectedString;
4188 insertMode << NORMAL;
4189 insertString << "Ensuring";
4192 insertMode << NORMAL;
4193 insertString << " instan";
4196 insertMode << NORMAL;
4197 insertString << "an ";
4200 insertMode << REPLACE_UNTIL_END;
4201 insertString << " unique instance.";
4203 expectedString << "Ensuring a unique instance.";
4204 expectedString << "Ensuring an instan";
4205 expectedString << "Ensuring instan";
4206 expectedString << "";
4208 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4218 void tst_qquicktextinput::undo()
4220 QFETCH(QStringList, insertString);
4221 QFETCH(IntList, insertIndex);
4222 QFETCH(IntList, insertMode);
4223 QFETCH(QStringList, expectedString);
4225 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4226 QDeclarativeComponent textInputComponent(&engine);
4227 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4228 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4229 QVERIFY(textInput != 0);
4231 QQuickCanvas canvas;
4232 textInput->setParentItem(canvas.rootItem());
4234 canvas.requestActivateWindow();
4235 QTest::qWaitForWindowShown(&canvas);
4236 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4238 QVERIFY(!textInput->canUndo());
4240 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4244 // STEP 1: First build up an undo history by inserting or typing some strings...
4245 for (i = 0; i < insertString.size(); ++i) {
4246 if (insertIndex[i] > -1)
4247 textInput->setCursorPosition(insertIndex[i]);
4249 // experimental stuff
4250 if (insertMode[i] == REPLACE_UNTIL_END) {
4251 textInput->select(insertIndex[i], insertIndex[i] + 8);
4253 // This is what I actually want...
4254 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4257 for (int j = 0; j < insertString.at(i).length(); j++)
4258 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4261 QCOMPARE(spy.count(), 1);
4263 // STEP 2: Next call undo several times and see if we can restore to the previous state
4264 for (i = 0; i < expectedString.size() - 1; ++i) {
4265 QCOMPARE(textInput->text(), expectedString[i]);
4266 QVERIFY(textInput->canUndo());
4270 // STEP 3: Verify that we have undone everything
4271 QVERIFY(textInput->text().isEmpty());
4272 QVERIFY(!textInput->canUndo());
4273 QCOMPARE(spy.count(), 2);
4276 void tst_qquicktextinput::redo_data()
4278 QTest::addColumn<QStringList>("insertString");
4279 QTest::addColumn<IntList>("insertIndex");
4280 QTest::addColumn<QStringList>("expectedString");
4283 IntList insertIndex;
4284 QStringList insertString;
4285 QStringList expectedString;
4288 insertString << "World"; // World
4290 insertString << "Hello"; // HelloWorld
4292 insertString << "Well"; // WellHelloWorld
4294 insertString << "There"; // WellHelloThereWorld;
4296 expectedString << "World";
4297 expectedString << "HelloWorld";
4298 expectedString << "WellHelloWorld";
4299 expectedString << "WellHelloThereWorld";
4301 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4305 void tst_qquicktextinput::redo()
4307 QFETCH(QStringList, insertString);
4308 QFETCH(IntList, insertIndex);
4309 QFETCH(QStringList, expectedString);
4311 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4312 QDeclarativeComponent textInputComponent(&engine);
4313 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4314 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4315 QVERIFY(textInput != 0);
4317 QQuickCanvas canvas;
4318 textInput->setParentItem(canvas.rootItem());
4320 canvas.requestActivateWindow();
4321 QTest::qWaitForWindowShown(&canvas);
4322 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4324 QVERIFY(!textInput->canUndo());
4325 QVERIFY(!textInput->canRedo());
4327 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
4330 // inserts the diff strings at diff positions
4331 for (i = 0; i < insertString.size(); ++i) {
4332 if (insertIndex[i] > -1)
4333 textInput->setCursorPosition(insertIndex[i]);
4334 for (int j = 0; j < insertString.at(i).length(); j++)
4335 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4336 QVERIFY(textInput->canUndo());
4337 QVERIFY(!textInput->canRedo());
4340 QCOMPARE(spy.count(), 0);
4343 while (!textInput->text().isEmpty()) {
4344 QVERIFY(textInput->canUndo());
4346 QVERIFY(textInput->canRedo());
4349 QCOMPARE(spy.count(), 1);
4351 for (i = 0; i < expectedString.size(); ++i) {
4352 QVERIFY(textInput->canRedo());
4354 QCOMPARE(textInput->text() , expectedString[i]);
4355 QVERIFY(textInput->canUndo());
4357 QVERIFY(!textInput->canRedo());
4358 QCOMPARE(spy.count(), 2);
4361 void tst_qquicktextinput::undo_keypressevents_data()
4363 QTest::addColumn<KeyList>("keys");
4364 QTest::addColumn<QStringList>("expectedString");
4368 QStringList expectedString;
4371 << QKeySequence::MoveToStartOfLine
4378 << QKeySequence::MoveToEndOfLine
4381 expectedString << "BEVERYAFRAID!";
4382 expectedString << "BEVERYAFRAID";
4383 expectedString << "VERYAFRAID";
4384 expectedString << "AFRAID";
4386 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4389 QStringList expectedString;
4392 keys << "1234" << QKeySequence::MoveToStartOfLine
4394 << Qt::Key_Right << Qt::Key_Right
4396 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4400 expectedString << "12";
4401 expectedString << "1234";
4403 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4406 QStringList expectedString;
4410 << QKeySequence::MoveToStartOfLine
4412 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4414 << QKeySequence::Undo
4416 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
4419 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4422 expectedString << "AB";
4423 expectedString << "AB12";
4425 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4428 QStringList expectedString;
4433 << Qt::Key_Left << Qt::Key_Left
4437 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4438 // overwriting '1234' with '5'
4440 // undoing deletion of 'AB'
4441 << QKeySequence::Undo
4442 // overwriting '1234' with '6'
4445 expectedString << "ab6cd";
4446 // for versions previous to 3.2 we overwrite needed two undo operations
4447 expectedString << "ab1234cd";
4448 expectedString << "abcd";
4450 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4453 QStringList expectedString;
4458 << Qt::Key_Backspace;
4460 expectedString << "AB";
4461 expectedString << "ABC";
4463 QTest::newRow("Inserts,backspace") << keys << expectedString;
4466 QStringList expectedString;
4470 << Qt::Key_Backspace
4474 expectedString << "ABZ";
4475 expectedString << "AB";
4476 expectedString << "ABC";
4478 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4481 QStringList expectedString;
4484 keys << "123" << QKeySequence::MoveToStartOfLine
4486 << QKeySequence::SelectEndOfLine
4487 // overwriting '123' with 'ABC'
4490 expectedString << "ABC";
4491 // for versions previous to 3.2 we overwrite needed two undo operations
4492 expectedString << "123";
4494 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4498 void tst_qquicktextinput::undo_keypressevents()
4500 QFETCH(KeyList, keys);
4501 QFETCH(QStringList, expectedString);
4503 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4504 QDeclarativeComponent textInputComponent(&engine);
4505 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4506 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4507 QVERIFY(textInput != 0);
4509 QQuickCanvas canvas;
4510 textInput->setParentItem(canvas.rootItem());
4512 canvas.requestActivateWindow();
4513 QTest::qWaitForWindowShown(&canvas);
4514 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4516 simulateKeys(&canvas, keys);
4518 for (int i = 0; i < expectedString.size(); ++i) {
4519 QCOMPARE(textInput->text() , expectedString[i]);
4522 QVERIFY(textInput->text().isEmpty());
4525 void tst_qquicktextinput::QTBUG_19956()
4527 QFETCH(QString, url);
4529 QQuickView canvas(testFileUrl(url));
4531 canvas.requestActivateWindow();
4532 QTest::qWaitForWindowShown(&canvas);
4533 QVERIFY(canvas.rootObject() != 0);
4534 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4536 input->setFocus(true);
4537 QVERIFY(input->hasActiveFocus());
4539 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
4540 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4541 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
4542 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4544 canvas.rootObject()->setProperty("topvalue", 15);
4545 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
4546 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4548 canvas.rootObject()->setProperty("topvalue", 25);
4549 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
4550 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4552 canvas.rootObject()->setProperty("bottomvalue", 21);
4553 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
4554 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4556 canvas.rootObject()->setProperty("bottomvalue", 10);
4557 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4558 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4561 void tst_qquicktextinput::QTBUG_19956_regexp()
4563 QUrl url = testFileUrl("qtbug-19956regexp.qml");
4565 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
4566 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
4568 QQuickView canvas(url);
4570 canvas.requestActivateWindow();
4571 QTest::qWaitForWindowShown(&canvas);
4572 QVERIFY(canvas.rootObject() != 0);
4573 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4575 input->setFocus(true);
4576 QVERIFY(input->hasActiveFocus());
4578 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4579 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4580 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
4581 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4583 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
4584 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
4585 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4587 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4588 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4589 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4593 void tst_qquicktextinput::negativeDimensions()
4595 // Verify this doesn't assert during initialization.
4596 QDeclarativeComponent component(&engine, testFileUrl("negativeDimensions.qml"));
4597 QScopedPointer<QObject> o(component.create());
4599 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
4601 QCOMPARE(input->width(), qreal(-1));
4602 QCOMPARE(input->height(), qreal(-1));
4605 QTEST_MAIN(tst_qquicktextinput)
4607 #include "tst_qquicktextinput.moc"