1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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();
135 void passwordCharacter();
136 void cursorDelegate();
137 void cursorVisible();
138 void cursorRectangle();
140 void navigation_RTL();
142 void copyAndPasteKeySequence();
143 void canPasteEmpty();
147 void openInputPanel();
148 void setHAlignClearCache();
149 void focusOutClearSelection();
152 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
153 void passwordEchoDelay();
155 void geometrySignals();
156 void testQtQuick11Attributes();
157 void testQtQuick11Attributes_data();
159 void preeditAutoScroll();
160 void preeditCursorRectangle();
161 void inputContextMouseHandler();
162 void inputMethodComposing();
163 void cursorRectangleSize();
165 void keySequence_data();
172 void undo_keypressevents_data();
173 void undo_keypressevents();
176 void QTBUG_19956_data();
177 void QTBUG_19956_regexp();
180 void simulateKey(QQuickView *, int key);
182 void simulateKeys(QWindow *window, const QList<Key> &keys);
183 void simulateKeys(QWindow *window, const QKeySequence &sequence);
185 QDeclarativeEngine engine;
186 QStringList standard;
187 QStringList colorStrings;
190 typedef QList<int> IntList;
191 Q_DECLARE_METATYPE(IntList)
193 typedef QList<Key> KeyList;
194 Q_DECLARE_METATYPE(KeyList)
196 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
198 for (int i = 0; i < keys.count(); ++i) {
199 const int key = keys.at(i).first;
200 const int modifiers = key & Qt::KeyboardModifierMask;
201 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
203 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
204 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
206 QGuiApplication::sendEvent(window, &press);
207 QGuiApplication::sendEvent(window, &release);
211 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
213 for (uint i = 0; i < sequence.count(); ++i) {
214 const int key = sequence[i];
215 const int modifiers = key & Qt::KeyboardModifierMask;
217 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
221 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
223 for (uint i = 0; i < sequence.count(); ++i)
224 keys << Key(sequence[i], QChar());
228 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
230 for (int i = 0; i < N - 1; ++i) {
231 int key = QTest::asciiToKey(characters[i]);
232 QChar character = QLatin1Char(characters[i]);
233 keys << Key(key, character);
238 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
240 keys << Key(key, QChar());
244 void tst_qquicktextinput::cleanup()
246 // ensure not even skipped tests with custom input context leave it dangling
247 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
248 inputPanelPrivate->testContext = 0;
251 tst_qquicktextinput::tst_qquicktextinput()
253 standard << "the quick brown fox jumped over the lazy dog"
254 << "It's supercalifragisiticexpialidocious!"
259 colorStrings << "aliceblue"
273 void tst_qquicktextinput::text()
276 QDeclarativeComponent textinputComponent(&engine);
277 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
278 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
280 QVERIFY(textinputObject != 0);
281 QCOMPARE(textinputObject->text(), QString(""));
283 delete textinputObject;
286 for (int i = 0; i < standard.size(); i++)
288 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
289 QDeclarativeComponent textinputComponent(&engine);
290 textinputComponent.setData(componentStr.toLatin1(), QUrl());
291 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
293 QVERIFY(textinputObject != 0);
294 QCOMPARE(textinputObject->text(), standard.at(i));
296 delete textinputObject;
301 void tst_qquicktextinput::width()
303 // uses Font metrics to find the width for standard
305 QDeclarativeComponent textinputComponent(&engine);
306 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
307 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
309 QVERIFY(textinputObject != 0);
310 QCOMPARE(textinputObject->width(), 0.0);
312 delete textinputObject;
315 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
317 for (int i = 0; i < standard.size(); i++)
319 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
320 QDeclarativeComponent textinputComponent(&engine);
321 textinputComponent.setData(componentStr.toLatin1(), QUrl());
322 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
324 QString s = standard.at(i);
325 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
327 QTextLayout layout(s);
328 layout.setFont(textinputObject->font());
329 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
330 if (requiresUnhintedMetrics) {
332 option.setUseDesignMetrics(true);
333 layout.setTextOption(option);
336 layout.beginLayout();
338 QTextLine line = layout.createLine();
345 qreal metricWidth = ceil(layout.boundingRect().width());
347 QVERIFY(textinputObject != 0);
348 int delta = abs(int(int(textinputObject->width()) - metricWidth));
349 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
351 delete textinputObject;
355 void tst_qquicktextinput::font()
357 //test size, then bold, then italic, then family
359 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
360 QDeclarativeComponent textinputComponent(&engine);
361 textinputComponent.setData(componentStr.toLatin1(), QUrl());
362 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
364 QVERIFY(textinputObject != 0);
365 QCOMPARE(textinputObject->font().pointSize(), 40);
366 QCOMPARE(textinputObject->font().bold(), false);
367 QCOMPARE(textinputObject->font().italic(), false);
369 delete textinputObject;
373 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; 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().bold(), true);
380 QCOMPARE(textinputObject->font().italic(), false);
382 delete textinputObject;
386 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
387 QDeclarativeComponent textinputComponent(&engine);
388 textinputComponent.setData(componentStr.toLatin1(), QUrl());
389 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
391 QVERIFY(textinputObject != 0);
392 QCOMPARE(textinputObject->font().italic(), true);
393 QCOMPARE(textinputObject->font().bold(), false);
395 delete textinputObject;
399 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
400 QDeclarativeComponent textinputComponent(&engine);
401 textinputComponent.setData(componentStr.toLatin1(), QUrl());
402 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
404 QVERIFY(textinputObject != 0);
405 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
406 QCOMPARE(textinputObject->font().bold(), false);
407 QCOMPARE(textinputObject->font().italic(), false);
409 delete textinputObject;
413 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; 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(""));
421 delete textinputObject;
425 void tst_qquicktextinput::color()
428 for (int i = 0; i < colorStrings.size(); i++)
430 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
431 QDeclarativeComponent textinputComponent(&engine);
432 textinputComponent.setData(componentStr.toLatin1(), QUrl());
433 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
434 QVERIFY(textinputObject != 0);
435 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
437 delete textinputObject;
440 //test selection color
441 for (int i = 0; i < colorStrings.size(); i++)
443 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
444 QDeclarativeComponent textinputComponent(&engine);
445 textinputComponent.setData(componentStr.toLatin1(), QUrl());
446 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
447 QVERIFY(textinputObject != 0);
448 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
450 delete textinputObject;
453 //test selected text color
454 for (int i = 0; i < colorStrings.size(); i++)
456 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
457 QDeclarativeComponent textinputComponent(&engine);
458 textinputComponent.setData(componentStr.toLatin1(), QUrl());
459 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
460 QVERIFY(textinputObject != 0);
461 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
463 delete textinputObject;
467 QString colorStr = "#AA001234";
468 QColor testColor("#001234");
469 testColor.setAlpha(170);
471 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
472 QDeclarativeComponent textinputComponent(&engine);
473 textinputComponent.setData(componentStr.toLatin1(), QUrl());
474 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
476 QVERIFY(textinputObject != 0);
477 QCOMPARE(textinputObject->color(), testColor);
479 delete textinputObject;
483 void tst_qquicktextinput::wrap()
486 // for specified width and wrap set true
488 QDeclarativeComponent textComponent(&engine);
489 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
490 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
491 textHeight = textObject->height();
493 QVERIFY(textObject != 0);
494 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
495 QCOMPARE(textObject->width(), 300.);
500 for (int i = 0; i < standard.count(); i++) {
501 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
502 QDeclarativeComponent textComponent(&engine);
503 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
504 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
506 QVERIFY(textObject != 0);
507 QCOMPARE(textObject->width(), 30.);
508 QVERIFY(textObject->height() > textHeight);
510 int oldHeight = textObject->height();
511 textObject->setWidth(100);
512 QVERIFY(textObject->height() < oldHeight);
518 void tst_qquicktextinput::selection()
520 QString testStr = standard[0];
521 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
522 QDeclarativeComponent textinputComponent(&engine);
523 textinputComponent.setData(componentStr.toLatin1(), QUrl());
524 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
525 QVERIFY(textinputObject != 0);
528 //Test selection follows cursor
529 for (int i=0; i<= testStr.size(); i++) {
530 textinputObject->setCursorPosition(i);
531 QCOMPARE(textinputObject->cursorPosition(), i);
532 QCOMPARE(textinputObject->selectionStart(), i);
533 QCOMPARE(textinputObject->selectionEnd(), i);
534 QVERIFY(textinputObject->selectedText().isNull());
537 textinputObject->setCursorPosition(0);
538 QVERIFY(textinputObject->cursorPosition() == 0);
539 QVERIFY(textinputObject->selectionStart() == 0);
540 QVERIFY(textinputObject->selectionEnd() == 0);
541 QVERIFY(textinputObject->selectedText().isNull());
543 // Verify invalid positions are ignored.
544 textinputObject->setCursorPosition(-1);
545 QVERIFY(textinputObject->cursorPosition() == 0);
546 QVERIFY(textinputObject->selectionStart() == 0);
547 QVERIFY(textinputObject->selectionEnd() == 0);
548 QVERIFY(textinputObject->selectedText().isNull());
550 textinputObject->setCursorPosition(textinputObject->text().count()+1);
551 QVERIFY(textinputObject->cursorPosition() == 0);
552 QVERIFY(textinputObject->selectionStart() == 0);
553 QVERIFY(textinputObject->selectionEnd() == 0);
554 QVERIFY(textinputObject->selectedText().isNull());
557 for (int i=0; i<= testStr.size(); i++) {
558 textinputObject->select(0,i);
559 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
561 for (int i=0; i<= testStr.size(); i++) {
562 textinputObject->select(i,testStr.size());
563 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
566 textinputObject->setCursorPosition(0);
567 QVERIFY(textinputObject->cursorPosition() == 0);
568 QVERIFY(textinputObject->selectionStart() == 0);
569 QVERIFY(textinputObject->selectionEnd() == 0);
570 QVERIFY(textinputObject->selectedText().isNull());
572 //Test Error Ignoring behaviour
573 textinputObject->setCursorPosition(0);
574 QVERIFY(textinputObject->selectedText().isNull());
575 textinputObject->select(-10,0);
576 QVERIFY(textinputObject->selectedText().isNull());
577 textinputObject->select(100,110);
578 QVERIFY(textinputObject->selectedText().isNull());
579 textinputObject->select(0,-10);
580 QVERIFY(textinputObject->selectedText().isNull());
581 textinputObject->select(0,100);
582 QVERIFY(textinputObject->selectedText().isNull());
583 textinputObject->select(0,10);
584 QVERIFY(textinputObject->selectedText().size() == 10);
585 textinputObject->select(-10,10);
586 QVERIFY(textinputObject->selectedText().size() == 10);
587 textinputObject->select(100,101);
588 QVERIFY(textinputObject->selectedText().size() == 10);
589 textinputObject->select(0,-10);
590 QVERIFY(textinputObject->selectedText().size() == 10);
591 textinputObject->select(0,100);
592 QVERIFY(textinputObject->selectedText().size() == 10);
594 textinputObject->deselect();
595 QVERIFY(textinputObject->selectedText().isNull());
596 textinputObject->select(0,10);
597 QVERIFY(textinputObject->selectedText().size() == 10);
598 textinputObject->deselect();
599 QVERIFY(textinputObject->selectedText().isNull());
601 // test input method selection
602 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
603 textinputObject->setFocus(true);
605 QList<QInputMethodEvent::Attribute> attributes;
606 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
607 QInputMethodEvent event("", attributes);
608 QApplication::sendEvent(textinputObject, &event);
610 QCOMPARE(selectionSpy.count(), 1);
611 QCOMPARE(textinputObject->selectionStart(), 12);
612 QCOMPARE(textinputObject->selectionEnd(), 17);
614 delete textinputObject;
617 void tst_qquicktextinput::isRightToLeft_data()
619 QTest::addColumn<QString>("text");
620 QTest::addColumn<bool>("emptyString");
621 QTest::addColumn<bool>("firstCharacter");
622 QTest::addColumn<bool>("lastCharacter");
623 QTest::addColumn<bool>("middleCharacter");
624 QTest::addColumn<bool>("startString");
625 QTest::addColumn<bool>("midString");
626 QTest::addColumn<bool>("endString");
628 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
629 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
630 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
631 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
632 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
633 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;
634 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
637 void tst_qquicktextinput::isRightToLeft()
639 QFETCH(QString, text);
640 QFETCH(bool, emptyString);
641 QFETCH(bool, firstCharacter);
642 QFETCH(bool, lastCharacter);
643 QFETCH(bool, middleCharacter);
644 QFETCH(bool, startString);
645 QFETCH(bool, midString);
646 QFETCH(bool, endString);
648 QQuickTextInput textInput;
649 textInput.setText(text);
651 // first test that the right string is delivered to the QString::isRightToLeft()
652 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
653 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
654 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
655 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
656 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
657 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
659 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
660 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
662 // then test that the feature actually works
663 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
664 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
665 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
666 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
667 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
668 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
670 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
671 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
674 void tst_qquicktextinput::moveCursorSelection_data()
676 QTest::addColumn<QString>("testStr");
677 QTest::addColumn<int>("cursorPosition");
678 QTest::addColumn<int>("movePosition");
679 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
680 QTest::addColumn<int>("selectionStart");
681 QTest::addColumn<int>("selectionEnd");
682 QTest::addColumn<bool>("reversible");
684 // () contains the text selected by the cursor.
685 // <> contains the actual selection.
687 QTest::newRow("(t)he|characters")
688 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
689 QTest::newRow("do(g)|characters")
690 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
691 QTest::newRow("jum(p)ed|characters")
692 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
693 QTest::newRow("jumped( )over|characters")
694 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
695 QTest::newRow("(the )|characters")
696 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
697 QTest::newRow("( dog)|characters")
698 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
699 QTest::newRow("( jumped )|characters")
700 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
701 QTest::newRow("th(e qu)ick|characters")
702 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
703 QTest::newRow("la(zy d)og|characters")
704 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
705 QTest::newRow("jum(ped ov)er|characters")
706 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
707 QTest::newRow("()the|characters")
708 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
709 QTest::newRow("dog()|characters")
710 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
711 QTest::newRow("jum()ped|characters")
712 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
714 QTest::newRow("<(t)he>|words")
715 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
716 QTest::newRow("<do(g)>|words")
717 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
718 QTest::newRow("<jum(p)ed>|words")
719 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
720 QTest::newRow("<jumped( )>over|words,ltr")
721 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
722 QTest::newRow("jumped<( )over>|words,rtl")
723 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
724 QTest::newRow("<(the )>quick|words,ltr")
725 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
726 QTest::newRow("<(the )quick>|words,rtl")
727 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
728 QTest::newRow("<lazy( dog)>|words,ltr")
729 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
730 QTest::newRow("lazy<( dog)>|words,rtl")
731 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
732 QTest::newRow("<fox( jumped )>over|words,ltr")
733 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
734 QTest::newRow("fox<( jumped )over>|words,rtl")
735 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
736 QTest::newRow("<th(e qu)ick>|words")
737 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
738 QTest::newRow("<la(zy d)og|words>")
739 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
740 QTest::newRow("<jum(ped ov)er>|words")
741 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
742 QTest::newRow("<()>the|words")
743 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
744 QTest::newRow("dog<()>|words")
745 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
746 QTest::newRow("jum<()>ped|words")
747 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
749 QTest::newRow("Hello<(,)> |words")
750 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
751 QTest::newRow("Hello<(, )>world|words,ltr")
752 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
753 QTest::newRow("Hello<(, )world>|words,rtl")
754 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
755 QTest::newRow("<Hel(lo, )>world|words,ltr")
756 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
757 QTest::newRow("<Hel(lo, )world>|words,rtl")
758 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
759 QTest::newRow("<Hel(lo)>,|words")
760 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
761 QTest::newRow("Hello<()>,|words")
762 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
763 QTest::newRow("Hello,<()>|words")
764 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
765 QTest::newRow("Hello<,( )>world|words,ltr")
766 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
767 QTest::newRow("Hello,<( )world>|words,rtl")
768 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
769 QTest::newRow("Hello<,( world)>|words,ltr")
770 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
771 QTest::newRow("Hello,<( world)>|words,rtl")
772 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
773 QTest::newRow("Hello<,( world!)>|words,ltr")
774 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
775 QTest::newRow("Hello,<( world!)>|words,rtl")
776 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
777 QTest::newRow("Hello<(, world!)>|words")
778 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
779 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
781 // QTest::newRow("world<(!)>|words")
782 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
783 QTest::newRow("world!<()>)|words")
784 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
785 QTest::newRow("world<()>!)|words")
786 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
788 QTest::newRow("<(,)>olleH |words")
789 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
790 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
791 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
792 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
793 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
794 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
795 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
796 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
797 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
798 QTest::newRow(",<(ol)leH>,|words")
799 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
800 QTest::newRow(",<()>olleH|words")
801 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
802 QTest::newRow("<()>,olleH|words")
803 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
804 QTest::newRow("<dlrow( )>,olleH|words,ltr")
805 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
806 QTest::newRow("dlrow<( ),>olleH|words,rtl")
807 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
808 QTest::newRow("<(dlrow )>,olleH|words,ltr")
809 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
810 QTest::newRow("<(dlrow ),>olleH|words,rtl")
811 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
812 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
813 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
814 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
815 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
816 QTest::newRow("(!dlrow ,)olleH|words")
817 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
818 QTest::newRow("<(!)>dlrow|words")
819 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
820 QTest::newRow("<()>!dlrow|words")
821 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
822 QTest::newRow("!<()>dlrow|words")
823 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
825 QTest::newRow(" <s(pac)ey> text |words")
826 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
827 QTest::newRow(" spacey <t(ex)t> |words")
828 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
829 QTest::newRow("<( )>spacey text |words|ltr")
830 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
831 QTest::newRow("<( )spacey> text |words|rtl")
832 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
833 QTest::newRow("spacey <text( )>|words|ltr")
834 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
836 // QTest::newRow("spacey text<( )>|words|rtl")
837 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
838 QTest::newRow("<()> spacey text |words")
839 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
840 QTest::newRow(" spacey text <()>|words")
841 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
844 void tst_qquicktextinput::moveCursorSelection()
846 QFETCH(QString, testStr);
847 QFETCH(int, cursorPosition);
848 QFETCH(int, movePosition);
849 QFETCH(QQuickTextInput::SelectionMode, mode);
850 QFETCH(int, selectionStart);
851 QFETCH(int, selectionEnd);
852 QFETCH(bool, reversible);
854 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
855 QDeclarativeComponent textinputComponent(&engine);
856 textinputComponent.setData(componentStr.toLatin1(), QUrl());
857 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
858 QVERIFY(textinputObject != 0);
860 textinputObject->setCursorPosition(cursorPosition);
861 textinputObject->moveCursorSelection(movePosition, mode);
863 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
864 QCOMPARE(textinputObject->selectionStart(), selectionStart);
865 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
868 textinputObject->setCursorPosition(movePosition);
869 textinputObject->moveCursorSelection(cursorPosition, mode);
871 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
872 QCOMPARE(textinputObject->selectionStart(), selectionStart);
873 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
876 delete textinputObject;
879 void tst_qquicktextinput::moveCursorSelectionSequence_data()
881 QTest::addColumn<QString>("testStr");
882 QTest::addColumn<int>("cursorPosition");
883 QTest::addColumn<int>("movePosition1");
884 QTest::addColumn<int>("movePosition2");
885 QTest::addColumn<int>("selection1Start");
886 QTest::addColumn<int>("selection1End");
887 QTest::addColumn<int>("selection2Start");
888 QTest::addColumn<int>("selection2End");
890 // () contains the text selected by the cursor.
891 // <> contains the actual selection.
892 // ^ is the revised cursor position.
893 // {} contains the revised selection.
895 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
900 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
905 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
910 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
915 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
920 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
925 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
930 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
935 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
940 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
945 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
950 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
955 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
960 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
965 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
970 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
975 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
980 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
986 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
991 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
997 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1002 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1008 QTest::newRow("{<(^} sp)acey> text |ltr")
1013 QTest::newRow("{<( ^}sp)acey> text |ltr")
1018 QTest::newRow("<( {s^p)acey>} text |rtl")
1023 QTest::newRow("<( {^sp)acey>} text |rtl")
1029 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1035 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1037 // << 15 << 12 << 14
1040 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1046 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
1048 // << 12 << 15 << 14
1053 void tst_qquicktextinput::moveCursorSelectionSequence()
1055 QFETCH(QString, testStr);
1056 QFETCH(int, cursorPosition);
1057 QFETCH(int, movePosition1);
1058 QFETCH(int, movePosition2);
1059 QFETCH(int, selection1Start);
1060 QFETCH(int, selection1End);
1061 QFETCH(int, selection2Start);
1062 QFETCH(int, selection2End);
1064 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1065 QDeclarativeComponent textinputComponent(&engine);
1066 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1067 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1068 QVERIFY(textinputObject != 0);
1070 textinputObject->setCursorPosition(cursorPosition);
1072 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1073 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1074 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1075 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1077 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1078 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1079 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1080 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1082 delete textinputObject;
1085 void tst_qquicktextinput::dragMouseSelection()
1087 QString qmlfile = testFile("mouseselection_true.qml");
1089 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1092 canvas.requestActivateWindow();
1093 QTest::qWaitForWindowShown(&canvas);
1095 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1097 QVERIFY(canvas.rootObject() != 0);
1098 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1099 QVERIFY(textInputObject != 0);
1101 // press-and-drag-and-release from x1 to x2
1104 int y = textInputObject->height()/2;
1105 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1106 QTest::mouseMove(&canvas, QPoint(x2, y));
1107 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1110 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1111 QVERIFY(str1.length() > 3);
1113 // press and drag the current selection.
1116 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1117 QTest::mouseMove(&canvas, QPoint(x2, y));
1118 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1120 QString str2 = textInputObject->selectedText();
1121 QVERIFY(str2.length() > 3);
1123 QVERIFY(str1 != str2);
1126 void tst_qquicktextinput::mouseSelectionMode_data()
1128 QTest::addColumn<QString>("qmlfile");
1129 QTest::addColumn<bool>("selectWords");
1132 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1133 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1134 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1137 void tst_qquicktextinput::mouseSelectionMode()
1139 QFETCH(QString, qmlfile);
1140 QFETCH(bool, selectWords);
1142 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1144 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1147 canvas.requestActivateWindow();
1148 QTest::qWaitForWindowShown(&canvas);
1149 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1151 QVERIFY(canvas.rootObject() != 0);
1152 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1153 QVERIFY(textInputObject != 0);
1155 // press-and-drag-and-release from x1 to x2
1158 int y = textInputObject->height()/2;
1159 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1160 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1161 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1164 QTRY_COMPARE(textInputObject->selectedText(), text);
1166 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1167 QVERIFY(textInputObject->selectedText() != text);
1171 void tst_qquicktextinput::horizontalAlignment_data()
1173 QTest::addColumn<int>("hAlign");
1174 QTest::addColumn<QString>("expectfile");
1176 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1177 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1178 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1181 void tst_qquicktextinput::horizontalAlignment()
1183 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1185 QFETCH(int, hAlign);
1186 QFETCH(QString, expectfile);
1188 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1191 canvas.requestActivateWindow();
1192 QTest::qWaitForWindowShown(&canvas);
1193 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1194 QObject *ob = canvas.rootObject();
1196 ob->setProperty("horizontalAlignment",hAlign);
1197 QImage actual = canvas.grabFrameBuffer();
1199 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1201 QImage expect(expectfile);
1203 QCOMPARE(actual,expect);
1206 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1208 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1209 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1210 QVERIFY(textInput != 0);
1213 const QString rtlText = textInput->text();
1215 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1216 QVERIFY(textInputPrivate != 0);
1217 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1219 // implicit alignment should follow the reading direction of RTL text
1220 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1221 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1222 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1224 // explicitly left aligned
1225 textInput->setHAlign(QQuickTextInput::AlignLeft);
1226 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1227 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1228 QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1230 // explicitly right aligned
1231 textInput->setHAlign(QQuickTextInput::AlignRight);
1232 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1233 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1234 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1236 // explicitly center aligned
1237 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1238 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1239 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1240 QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1241 QVERIFY(textInput->boundingRect().right() > canvas.width()/2);
1243 // reseted alignment should go back to following the text reading direction
1244 textInput->resetHAlign();
1245 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1246 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1247 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1249 // mirror the text item
1250 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1252 // mirrored implicit alignment should continue to follow the reading direction of the text
1253 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1254 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1255 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1257 // explicitly right aligned behaves as left aligned
1258 textInput->setHAlign(QQuickTextInput::AlignRight);
1259 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1260 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1261 QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1263 // mirrored explicitly left aligned behaves as right aligned
1264 textInput->setHAlign(QQuickTextInput::AlignLeft);
1265 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1266 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1267 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1269 // disable mirroring
1270 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1271 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1272 textInput->resetHAlign();
1274 // English text should be implicitly left aligned
1275 textInput->setText("Hello world!");
1276 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1277 QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1279 canvas.requestActivateWindow();
1280 QTest::qWaitForWindowShown(&canvas);
1281 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1283 // If there is no commited text, the preedit text should determine the alignment.
1284 textInput->setText(QString());
1285 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1286 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1287 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1288 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1290 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1291 // redundant as an actual input method may take care of it.
1292 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1294 // empty text with implicit alignment follows the system locale-based
1295 // keyboard input direction from QGuiApplication::keyboardInputDirection
1296 textInput->setText("");
1297 QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1298 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1299 if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1300 QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1302 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1303 textInput->setHAlign(QQuickTextInput::AlignRight);
1304 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1305 QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1307 QString componentStr = "import QtQuick 2.0\nTextInput {}";
1308 QDeclarativeComponent textComponent(&engine);
1309 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1310 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1311 QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1312 QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1316 void tst_qquicktextinput::verticalAlignment()
1318 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1319 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1320 QVERIFY(textInput != 0);
1323 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1324 QVERIFY(textInputPrivate != 0);
1326 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1327 QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1330 textInput->setVAlign(QQuickTextInput::AlignBottom);
1331 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1332 QVERIFY(textInput->boundingRect().top () > canvas.height() / 2);
1334 // explicitly center aligned
1335 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1336 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1337 QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1338 QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1341 void tst_qquicktextinput::positionAt()
1343 QQuickView canvas(testFileUrl("positionAt.qml"));
1344 QVERIFY(canvas.rootObject() != 0);
1346 canvas.requestActivateWindow();
1347 QTest::qWaitForWindowShown(&canvas);
1349 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1350 QVERIFY(textinputObject != 0);
1352 // Check autoscrolled...
1354 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1356 QTextLayout layout(textinputObject->text());
1357 layout.setFont(textinputObject->font());
1359 if (!qmlDisableDistanceField()) {
1361 option.setUseDesignMetrics(true);
1362 layout.setTextOption(option);
1364 layout.beginLayout();
1365 QTextLine line = layout.createLine();
1368 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1369 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1370 int textWidth = floor(line.horizontalAdvance());
1372 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1373 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1375 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1376 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1377 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1379 // Check without autoscroll...
1380 textinputObject->setAutoScroll(false);
1381 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1383 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1384 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1386 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1387 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1389 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1390 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1391 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1393 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1394 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1396 QString preeditText = textinputObject->text().mid(0, pos);
1397 textinputObject->setText(textinputObject->text().mid(pos));
1398 textinputObject->setCursorPosition(0);
1400 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1401 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1403 // Check all points within the preedit text return the same position.
1404 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1405 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1406 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1408 // Verify positioning returns to normal after the preedit text.
1409 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1410 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1412 { QInputMethodEvent inputEvent;
1413 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1416 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1418 const qreal y0 = line.height() / 2;
1419 const qreal y1 = line.height() * 3 / 2;
1421 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1422 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1424 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1425 QVERIFY(newLinePos > pos);
1426 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1429 void tst_qquicktextinput::maxLength()
1431 QQuickView canvas(testFileUrl("maxLength.qml"));
1432 QVERIFY(canvas.rootObject() != 0);
1434 canvas.requestActivateWindow();
1435 QTest::qWaitForWindowShown(&canvas);
1437 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1438 QVERIFY(textinputObject != 0);
1439 QVERIFY(textinputObject->text().isEmpty());
1440 QVERIFY(textinputObject->maxLength() == 10);
1441 foreach (const QString &str, standard) {
1442 QVERIFY(textinputObject->text().length() <= 10);
1443 textinputObject->setText(str);
1444 QVERIFY(textinputObject->text().length() <= 10);
1447 textinputObject->setText("");
1448 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1449 for (int i=0; i<20; i++) {
1450 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1451 //simulateKey(&canvas, Qt::Key_A);
1452 QTest::keyPress(&canvas, Qt::Key_A);
1453 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1458 void tst_qquicktextinput::masks()
1460 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1461 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1462 QQuickView canvas(testFileUrl("masks.qml"));
1464 canvas.requestActivateWindow();
1465 QVERIFY(canvas.rootObject() != 0);
1466 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1467 QVERIFY(textinputObject != 0);
1468 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1469 QVERIFY(textinputObject->text().length() == 0);
1470 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1471 for (int i=0; i<10; i++) {
1472 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1473 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1474 //simulateKey(&canvas, Qt::Key_A);
1475 QTest::keyPress(&canvas, Qt::Key_A);
1476 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1481 void tst_qquicktextinput::validators()
1483 // Note that this test assumes that the validators are working properly
1484 // so you may need to run their tests first. All validators are checked
1485 // here to ensure that their exposure to QML is working.
1487 QQuickView canvas(testFileUrl("validators.qml"));
1489 canvas.requestActivateWindow();
1491 QVERIFY(canvas.rootObject() != 0);
1493 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1495 intInput->setFocus(true);
1496 QTRY_VERIFY(intInput->hasActiveFocus());
1497 QTest::keyPress(&canvas, Qt::Key_1);
1498 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1500 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1501 QCOMPARE(intInput->hasAcceptableInput(), false);
1502 QTest::keyPress(&canvas, Qt::Key_2);
1503 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1505 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1506 QCOMPARE(intInput->hasAcceptableInput(), false);
1507 QTest::keyPress(&canvas, Qt::Key_1);
1508 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1510 QCOMPARE(intInput->text(), QLatin1String("11"));
1511 QCOMPARE(intInput->hasAcceptableInput(), true);
1512 QTest::keyPress(&canvas, Qt::Key_0);
1513 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1515 QCOMPARE(intInput->text(), QLatin1String("11"));
1516 QCOMPARE(intInput->hasAcceptableInput(), true);
1518 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1519 QTRY_VERIFY(dblInput);
1520 dblInput->setFocus(true);
1521 QVERIFY(dblInput->hasActiveFocus() == true);
1522 QTest::keyPress(&canvas, Qt::Key_1);
1523 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1525 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1526 QCOMPARE(dblInput->hasAcceptableInput(), false);
1527 QTest::keyPress(&canvas, Qt::Key_2);
1528 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1530 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1531 QCOMPARE(dblInput->hasAcceptableInput(), true);
1532 QTest::keyPress(&canvas, Qt::Key_Period);
1533 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1535 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1536 QCOMPARE(dblInput->hasAcceptableInput(), true);
1537 QTest::keyPress(&canvas, Qt::Key_1);
1538 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1540 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1541 QCOMPARE(dblInput->hasAcceptableInput(), true);
1542 QTest::keyPress(&canvas, Qt::Key_1);
1543 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1545 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1546 QCOMPARE(dblInput->hasAcceptableInput(), true);
1547 QTest::keyPress(&canvas, Qt::Key_1);
1548 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1550 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1551 QCOMPARE(dblInput->hasAcceptableInput(), true);
1553 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1554 QTRY_VERIFY(strInput);
1555 strInput->setFocus(true);
1556 QVERIFY(strInput->hasActiveFocus() == true);
1557 QTest::keyPress(&canvas, Qt::Key_1);
1558 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1560 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1561 QCOMPARE(strInput->hasAcceptableInput(), false);
1562 QTest::keyPress(&canvas, Qt::Key_A);
1563 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1565 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1566 QCOMPARE(strInput->hasAcceptableInput(), false);
1567 QTest::keyPress(&canvas, Qt::Key_A);
1568 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1570 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1571 QCOMPARE(strInput->hasAcceptableInput(), true);
1572 QTest::keyPress(&canvas, Qt::Key_A);
1573 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1575 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1576 QCOMPARE(strInput->hasAcceptableInput(), true);
1577 QTest::keyPress(&canvas, Qt::Key_A);
1578 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1580 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1581 QCOMPARE(strInput->hasAcceptableInput(), true);
1582 QTest::keyPress(&canvas, Qt::Key_A);
1583 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1585 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1586 QCOMPARE(strInput->hasAcceptableInput(), true);
1589 void tst_qquicktextinput::inputMethods()
1591 QQuickView canvas(testFileUrl("inputmethods.qml"));
1593 canvas.requestActivateWindow();
1594 QTest::qWaitForWindowShown(&canvas);
1596 // test input method hints
1597 QVERIFY(canvas.rootObject() != 0);
1598 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1599 QVERIFY(input != 0);
1600 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1601 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1602 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1604 input->setFocus(true);
1605 QVERIFY(input->hasActiveFocus() == true);
1606 // test that input method event is committed
1607 QInputMethodEvent event;
1608 event.setCommitString( "My ", -12, 0);
1609 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1610 QCOMPARE(input->text(), QString("My Hello world!"));
1612 input->setCursorPosition(2);
1613 event.setCommitString("Your", -2, 2);
1614 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1615 QCOMPARE(input->text(), QString("Your Hello world!"));
1616 QCOMPARE(input->cursorPosition(), 4);
1618 input->setCursorPosition(7);
1619 event.setCommitString("Goodbye", -2, 5);
1620 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1621 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1622 QCOMPARE(input->cursorPosition(), 12);
1624 input->setCursorPosition(8);
1625 event.setCommitString("Our", -8, 4);
1626 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1627 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1628 QCOMPARE(input->cursorPosition(), 7);
1630 // test that basic tentative commit gets to text property on preedit state
1632 QList<QInputMethodEvent::Attribute> attributes;
1633 QInputMethodEvent preeditEvent("test", attributes);
1634 preeditEvent.setTentativeCommitString("test");
1635 QApplication::sendEvent(input, &preeditEvent);
1636 QCOMPARE(input->text(), QString("test"));
1638 // tentative commit not allowed present in surrounding text
1639 QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
1640 QApplication::sendEvent(input, &queryEvent);
1641 QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
1643 // if text with tentative commit does not validate, not allowed to be part of text property
1644 input->setText(""); // ensure input state is reset
1645 QValidator *validator = new QIntValidator(0, 100);
1646 input->setValidator(validator);
1647 QApplication::sendEvent(input, &preeditEvent);
1648 QCOMPARE(input->text(), QString(""));
1649 input->setValidator(0);
1652 // input should reset selection even if replacement parameters are out of bounds
1653 input->setText("text");
1654 input->setCursorPosition(0);
1655 input->moveCursorSelection(input->text().length());
1656 event.setCommitString("replacement", -input->text().length(), input->text().length());
1657 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1658 QCOMPARE(input->selectionStart(), input->selectionEnd());
1662 TextInput element should only handle left/right keys until the cursor reaches
1663 the extent of the text, then they should ignore the keys.
1666 void tst_qquicktextinput::navigation()
1668 QQuickView canvas(testFileUrl("navigation.qml"));
1670 canvas.requestActivateWindow();
1672 QVERIFY(canvas.rootObject() != 0);
1674 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1676 QVERIFY(input != 0);
1677 input->setCursorPosition(0);
1678 QTRY_VERIFY(input->hasActiveFocus() == true);
1679 simulateKey(&canvas, Qt::Key_Left);
1680 QVERIFY(input->hasActiveFocus() == false);
1681 simulateKey(&canvas, Qt::Key_Right);
1682 QVERIFY(input->hasActiveFocus() == true);
1683 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1684 input->setCursorPosition(input->text().length());
1685 input->select(0,input->text().length());
1686 QVERIFY(input->selectionStart() != input->selectionEnd());
1687 simulateKey(&canvas, Qt::Key_Right);
1688 QVERIFY(input->selectionStart() == input->selectionEnd());
1689 QVERIFY(input->selectionStart() == input->text().length());
1690 QVERIFY(input->hasActiveFocus() == true);
1691 simulateKey(&canvas, Qt::Key_Right);
1692 QVERIFY(input->hasActiveFocus() == false);
1693 simulateKey(&canvas, Qt::Key_Left);
1694 QVERIFY(input->hasActiveFocus() == true);
1696 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1697 input->setCursorPosition(2);
1698 QCOMPARE(input->cursorPosition(),2);
1699 simulateKey(&canvas, Qt::Key_Up);
1700 QCOMPARE(input->cursorPosition(),2);
1701 simulateKey(&canvas, Qt::Key_Down);
1702 QCOMPARE(input->cursorPosition(),2);
1705 void tst_qquicktextinput::navigation_RTL()
1707 QQuickView canvas(testFileUrl("navigation.qml"));
1709 canvas.requestActivateWindow();
1711 QVERIFY(canvas.rootObject() != 0);
1713 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1715 QVERIFY(input != 0);
1716 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1717 input->setText(QString::fromUtf16(arabic_str, 11));
1719 input->setCursorPosition(0);
1720 QTRY_VERIFY(input->hasActiveFocus() == true);
1723 simulateKey(&canvas, Qt::Key_Right);
1724 QVERIFY(input->hasActiveFocus() == false);
1727 simulateKey(&canvas, Qt::Key_Left);
1728 QVERIFY(input->hasActiveFocus() == true);
1730 input->setCursorPosition(input->text().length());
1731 QVERIFY(input->hasActiveFocus() == true);
1734 simulateKey(&canvas, Qt::Key_Left);
1735 QVERIFY(input->hasActiveFocus() == false);
1738 simulateKey(&canvas, Qt::Key_Right);
1739 QVERIFY(input->hasActiveFocus() == true);
1742 void tst_qquicktextinput::copyAndPaste() {
1743 #ifndef QT_NO_CLIPBOARD
1747 PasteboardRef pasteboard;
1748 OSStatus status = PasteboardCreate(0, &pasteboard);
1749 if (status == noErr)
1750 CFRelease(pasteboard);
1752 QSKIP("This machine doesn't support the clipboard");
1756 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1757 QDeclarativeComponent textInputComponent(&engine);
1758 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1759 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1760 QVERIFY(textInput != 0);
1763 QCOMPARE(textInput->text().length(), 12);
1764 textInput->select(0, textInput->text().length());;
1766 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1767 QCOMPARE(textInput->selectedText().length(), 12);
1768 textInput->setCursorPosition(0);
1769 QVERIFY(textInput->canPaste());
1771 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1772 QCOMPARE(textInput->text().length(), 24);
1775 QVERIFY(textInput->canPaste());
1776 textInput->setReadOnly(true);
1777 QVERIFY(!textInput->canPaste());
1778 textInput->setReadOnly(false);
1779 QVERIFY(textInput->canPaste());
1782 textInput->setCursorPosition(0);
1783 textInput->selectWord();
1784 QCOMPARE(textInput->selectedText(), QString("Hello"));
1786 // select all and cut
1787 textInput->selectAll();
1789 QCOMPARE(textInput->text().length(), 0);
1791 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1792 QCOMPARE(textInput->text().length(), 24);
1794 // clear copy buffer
1795 QClipboard *clipboard = QGuiApplication::clipboard();
1798 QVERIFY(!textInput->canPaste());
1800 // test that copy functionality is disabled
1801 // when echo mode is set to hide text/password mode
1804 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1805 textInput->setEchoMode(echoMode);
1806 textInput->setText("My password");
1807 textInput->select(0, textInput->text().length());;
1809 if (echoMode == QQuickTextInput::Normal) {
1810 QVERIFY(!clipboard->text().isEmpty());
1811 QCOMPARE(clipboard->text(), QString("My password"));
1814 QVERIFY(clipboard->text().isEmpty());
1823 void tst_qquicktextinput::copyAndPasteKeySequence() {
1824 #ifndef QT_NO_CLIPBOARD
1828 PasteboardRef pasteboard;
1829 OSStatus status = PasteboardCreate(0, &pasteboard);
1830 if (status == noErr)
1831 CFRelease(pasteboard);
1833 QSKIP("This machine doesn't support the clipboard");
1837 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
1838 QDeclarativeComponent textInputComponent(&engine);
1839 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1840 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1841 QVERIFY(textInput != 0);
1843 QQuickCanvas canvas;
1844 textInput->setParentItem(canvas.rootItem());
1846 canvas.requestActivateWindow();
1847 QTest::qWaitForWindowShown(&canvas);
1848 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
1851 QVERIFY(textInput->hasActiveFocus());
1852 QCOMPARE(textInput->text().length(), 12);
1853 textInput->select(0, textInput->text().length());
1854 simulateKeys(&canvas, QKeySequence::Copy);
1855 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1856 QCOMPARE(textInput->selectedText().length(), 12);
1857 textInput->setCursorPosition(0);
1858 QVERIFY(textInput->canPaste());
1859 simulateKeys(&canvas, QKeySequence::Paste);
1860 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1861 QCOMPARE(textInput->text().length(), 24);
1863 // select all and cut
1864 simulateKeys(&canvas, QKeySequence::SelectAll);
1865 simulateKeys(&canvas, QKeySequence::Cut);
1866 QCOMPARE(textInput->text().length(), 0);
1867 simulateKeys(&canvas, QKeySequence::Paste);
1868 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1869 QCOMPARE(textInput->text().length(), 24);
1871 // clear copy buffer
1872 QClipboard *clipboard = QGuiApplication::clipboard();
1875 QVERIFY(!textInput->canPaste());
1877 // test that copy functionality is disabled
1878 // when echo mode is set to hide text/password mode
1881 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1882 textInput->setEchoMode(echoMode);
1883 textInput->setText("My password");
1884 textInput->select(0, textInput->text().length());;
1885 simulateKeys(&canvas, QKeySequence::Copy);
1886 if (echoMode == QQuickTextInput::Normal) {
1887 QVERIFY(!clipboard->text().isEmpty());
1888 QCOMPARE(clipboard->text(), QString("My password"));
1891 QVERIFY(clipboard->text().isEmpty());
1900 void tst_qquicktextinput::canPasteEmpty() {
1901 #ifndef QT_NO_CLIPBOARD
1903 QGuiApplication::clipboard()->clear();
1905 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1906 QDeclarativeComponent textInputComponent(&engine);
1907 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1908 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1909 QVERIFY(textInput != 0);
1911 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1912 QCOMPARE(textInput->canPaste(), cp);
1917 void tst_qquicktextinput::canPaste() {
1918 #ifndef QT_NO_CLIPBOARD
1920 QGuiApplication::clipboard()->setText("Some text");
1922 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1923 QDeclarativeComponent textInputComponent(&engine);
1924 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1925 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1926 QVERIFY(textInput != 0);
1928 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1929 QCOMPARE(textInput->canPaste(), cp);
1934 void tst_qquicktextinput::passwordCharacter()
1936 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1937 QDeclarativeComponent textInputComponent(&engine);
1938 textInputComponent.setData(componentStr.toLatin1(), QUrl());
1939 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1940 QVERIFY(textInput != 0);
1942 textInput->setPasswordCharacter("X");
1943 qreal implicitWidth = textInput->implicitWidth();
1944 textInput->setPasswordCharacter(".");
1946 // QTBUG-12383 content is updated and redrawn
1947 QVERIFY(textInput->implicitWidth() < implicitWidth);
1952 void tst_qquicktextinput::cursorDelegate()
1954 QQuickView view(testFileUrl("cursorTest.qml"));
1956 view.requestActivateWindow();
1957 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1958 QVERIFY(textInputObject != 0);
1959 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1960 //Test Delegate gets created
1961 textInputObject->setFocus(true);
1962 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1963 QVERIFY(delegateObject);
1964 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1965 //Test Delegate gets moved
1966 for (int i=0; i<= textInputObject->text().length(); i++) {
1967 textInputObject->setCursorPosition(i);
1968 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1969 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1971 textInputObject->setCursorPosition(0);
1972 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1973 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1974 //Test Delegate gets deleted
1975 textInputObject->setCursorDelegate(0);
1976 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1979 void tst_qquicktextinput::cursorVisible()
1981 QQuickView view(testFileUrl("cursorVisible.qml"));
1983 view.requestActivateWindow();
1984 QTest::qWaitForWindowShown(&view);
1985 QTRY_COMPARE(&view, qGuiApp->focusWindow());
1987 QQuickTextInput input;
1988 input.componentComplete();
1989 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1991 QCOMPARE(input.isCursorVisible(), false);
1993 input.setCursorVisible(true);
1994 QCOMPARE(input.isCursorVisible(), true);
1995 QCOMPARE(spy.count(), 1);
1997 input.setCursorVisible(false);
1998 QCOMPARE(input.isCursorVisible(), false);
1999 QCOMPARE(spy.count(), 2);
2001 input.setFocus(true);
2002 QCOMPARE(input.isCursorVisible(), false);
2003 QCOMPARE(spy.count(), 2);
2005 input.setParentItem(view.rootObject());
2006 QCOMPARE(input.isCursorVisible(), true);
2007 QCOMPARE(spy.count(), 3);
2009 input.setFocus(false);
2010 QCOMPARE(input.isCursorVisible(), false);
2011 QCOMPARE(spy.count(), 4);
2013 input.setFocus(true);
2014 QCOMPARE(input.isCursorVisible(), true);
2015 QCOMPARE(spy.count(), 5);
2017 QQuickView alternateView;
2018 alternateView.show();
2019 alternateView.requestActivateWindow();
2020 QTest::qWaitForWindowShown(&alternateView);
2022 QCOMPARE(input.isCursorVisible(), false);
2023 QCOMPARE(spy.count(), 6);
2025 view.requestActivateWindow();
2026 QTest::qWaitForWindowShown(&view);
2027 QCOMPARE(input.isCursorVisible(), true);
2028 QCOMPARE(spy.count(), 7);
2031 void tst_qquicktextinput::cursorRectangle()
2034 QString text = "Hello World!";
2036 QQuickTextInput input;
2037 input.setText(text);
2038 input.componentComplete();
2040 QTextLayout layout(text);
2041 layout.setFont(input.font());
2042 if (!qmlDisableDistanceField()) {
2044 option.setUseDesignMetrics(true);
2045 layout.setTextOption(option);
2047 layout.beginLayout();
2048 QTextLine line = layout.createLine();
2051 input.setWidth(line.cursorToX(5, QTextLine::Leading));
2052 input.setHeight(qCeil(line.height() * 3 / 2));
2056 // some tolerance for different fonts.
2058 const int error = 2;
2060 const int error = 5;
2063 for (int i = 0; i <= 5; ++i) {
2064 input.setCursorPosition(i);
2065 r = input.cursorRectangle();
2067 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2068 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2069 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2072 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2073 QVERIFY(r.left() < input.width());
2074 QVERIFY(r.right() >= input.width() - error);
2076 for (int i = 6; i < text.length(); ++i) {
2077 input.setCursorPosition(i);
2078 QCOMPARE(r, input.cursorRectangle());
2079 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2082 for (int i = text.length() - 2; i >= 0; --i) {
2083 input.setCursorPosition(i);
2084 r = input.cursorRectangle();
2085 QCOMPARE(r.top(), 0);
2086 QVERIFY(r.right() >= 0);
2087 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2090 // Check vertical scrolling with word wrap.
2091 input.setWrapMode(QQuickTextInput::WordWrap);
2092 for (int i = 0; i <= 5; ++i) {
2093 input.setCursorPosition(i);
2094 r = input.cursorRectangle();
2096 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2097 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2098 QCOMPARE(r.top(), 0);
2099 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2102 input.setCursorPosition(6);
2103 r = input.cursorRectangle();
2104 QCOMPARE(r.left(), 0);
2105 QVERIFY(r.bottom() >= input.height() - error);
2107 for (int i = 7; i < text.length(); ++i) {
2108 input.setCursorPosition(i);
2109 r = input.cursorRectangle();
2110 QVERIFY(r.bottom() >= input.height() - error);
2113 for (int i = text.length() - 2; i >= 6; --i) {
2114 input.setCursorPosition(i);
2115 r = input.cursorRectangle();
2116 QVERIFY(r.bottom() >= input.height() - error);
2119 for (int i = 5; i >= 0; --i) {
2120 input.setCursorPosition(i);
2121 r = input.cursorRectangle();
2122 QCOMPARE(r.top(), 0);
2125 input.setText("Hi!");
2126 input.setHAlign(QQuickTextInput::AlignRight);
2127 r = input.cursorRectangle();
2128 QVERIFY(r.left() < input.width() + error);
2129 QVERIFY(r.right() >= input.width() - error);
2132 void tst_qquicktextinput::readOnly()
2134 QQuickView canvas(testFileUrl("readOnly.qml"));
2136 canvas.requestActivateWindow();
2138 QVERIFY(canvas.rootObject() != 0);
2140 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2142 QVERIFY(input != 0);
2143 QTRY_VERIFY(input->hasActiveFocus() == true);
2144 QVERIFY(input->isReadOnly() == true);
2145 QString initial = input->text();
2146 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2147 simulateKey(&canvas, k);
2148 simulateKey(&canvas, Qt::Key_Return);
2149 simulateKey(&canvas, Qt::Key_Space);
2150 simulateKey(&canvas, Qt::Key_Escape);
2151 QCOMPARE(input->text(), initial);
2153 input->setCursorPosition(3);
2154 input->setReadOnly(false);
2155 QCOMPARE(input->isReadOnly(), false);
2156 QCOMPARE(input->cursorPosition(), input->text().length());
2159 void tst_qquicktextinput::echoMode()
2161 QQuickView canvas(testFileUrl("echoMode.qml"));
2163 canvas.requestActivateWindow();
2164 QTest::qWaitForWindowShown(&canvas);
2165 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2167 QVERIFY(canvas.rootObject() != 0);
2169 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2171 QVERIFY(input != 0);
2172 QTRY_VERIFY(input->hasActiveFocus() == true);
2173 QString initial = input->text();
2174 Qt::InputMethodHints ref;
2175 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2176 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2177 QCOMPARE(input->displayText(), input->text());
2179 ref &= ~Qt::ImhHiddenText;
2180 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2181 QCOMPARE(input->inputMethodHints(), ref);
2182 input->setEchoMode(QQuickTextInput::NoEcho);
2183 QCOMPARE(input->text(), initial);
2184 QCOMPARE(input->displayText(), QLatin1String(""));
2185 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2187 ref |= Qt::ImhHiddenText;
2188 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2189 QCOMPARE(input->inputMethodHints(), ref);
2190 input->setEchoMode(QQuickTextInput::Password);
2192 ref |= Qt::ImhHiddenText;
2193 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2194 QCOMPARE(input->text(), initial);
2195 QCOMPARE(input->displayText(), QLatin1String("********"));
2196 QCOMPARE(input->inputMethodHints(), ref);
2197 input->setPasswordCharacter(QChar('Q'));
2198 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2199 QCOMPARE(input->text(), initial);
2200 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2201 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2202 //PasswordEchoOnEdit
2203 ref &= ~Qt::ImhHiddenText;
2204 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2205 QCOMPARE(input->inputMethodHints(), ref);
2206 QCOMPARE(input->text(), initial);
2207 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2208 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2209 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2210 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2211 QCOMPARE(input->text(), QLatin1String("a"));
2212 QCOMPARE(input->displayText(), QLatin1String("a"));
2213 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2214 input->setFocus(false);
2215 QVERIFY(input->hasActiveFocus() == false);
2216 QCOMPARE(input->displayText(), QLatin1String("Q"));
2217 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2218 input->setFocus(true);
2219 QVERIFY(input->hasActiveFocus());
2220 QInputMethodEvent inputEvent;
2221 inputEvent.setCommitString(initial);
2222 QGuiApplication::sendEvent(input, &inputEvent);
2223 QCOMPARE(input->text(), initial);
2224 QCOMPARE(input->displayText(), initial);
2225 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2228 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2229 void tst_qquicktextinput::passwordEchoDelay()
2231 QQuickView canvas(testFileUrl("echoMode.qml"));
2233 canvas.requestActivateWindow();
2234 QTest::qWaitForWindowShown(&canvas);
2235 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2237 QVERIFY(canvas.rootObject() != 0);
2239 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2241 QChar fillChar = QLatin1Char('*');
2243 input->setEchoMode(QQuickTextInput::Password);
2244 QCOMPARE(input->displayText(), QString(8, fillChar));
2245 input->setText(QString());
2246 QCOMPARE(input->displayText(), QString());
2248 QTest::keyPress(&canvas, '0');
2249 QTest::keyPress(&canvas, '1');
2250 QTest::keyPress(&canvas, '2');
2251 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2252 QTest::keyPress(&canvas, '3');
2253 QTest::keyPress(&canvas, '4');
2254 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2255 QTest::keyPress(&canvas, Qt::Key_Backspace);
2256 QCOMPARE(input->displayText(), QString(4, fillChar));
2257 QTest::keyPress(&canvas, '4');
2258 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2259 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2260 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2261 QTest::keyPress(&canvas, '5');
2262 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2263 input->setFocus(false);
2264 QVERIFY(!input->hasFocus());
2265 QCOMPARE(input->displayText(), QString(6, fillChar));
2266 input->setFocus(true);
2267 QTRY_VERIFY(input->hasFocus());
2268 QCOMPARE(input->displayText(), QString(6, fillChar));
2269 QTest::keyPress(&canvas, '6');
2270 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2272 QInputMethodEvent ev;
2273 ev.setCommitString(QLatin1String("7"));
2274 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2275 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2277 input->setCursorPosition(3);
2278 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2279 QTest::keyPress(&canvas, 'a');
2280 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2281 QTest::keyPress(&canvas, Qt::Key_Backspace);
2282 QCOMPARE(input->displayText(), QString(8, fillChar));
2287 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2289 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2290 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2292 QGuiApplication::sendEvent(view, &press);
2293 QGuiApplication::sendEvent(view, &release);
2297 void tst_qquicktextinput::openInputPanel()
2299 PlatformInputContext platformInputContext;
2300 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2301 inputPanelPrivate->testContext = &platformInputContext;
2303 QQuickView view(testFileUrl("openInputPanel.qml"));
2305 view.requestActivateWindow();
2306 QTest::qWaitForWindowShown(&view);
2307 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2309 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2312 // check default values
2313 QVERIFY(input->focusOnPress());
2314 QVERIFY(!input->hasActiveFocus());
2315 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2316 QCOMPARE(qApp->inputPanel()->visible(), false);
2318 // input panel should open on focus
2319 QPoint centerPoint(view.width()/2, view.height()/2);
2320 Qt::KeyboardModifiers noModifiers = 0;
2321 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2322 QGuiApplication::processEvents();
2323 QVERIFY(input->hasActiveFocus());
2324 QCOMPARE(qApp->inputPanel()->inputItem(), input);
2325 QCOMPARE(qApp->inputPanel()->visible(), true);
2326 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2328 // input panel should be re-opened when pressing already focused TextInput
2329 qApp->inputPanel()->hide();
2330 QCOMPARE(qApp->inputPanel()->visible(), false);
2331 QVERIFY(input->hasActiveFocus());
2332 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2333 QGuiApplication::processEvents();
2334 QCOMPARE(qApp->inputPanel()->visible(), true);
2335 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2337 // input panel should stay visible if focus is lost to another text inputor
2338 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2339 QQuickTextInput anotherInput;
2340 anotherInput.componentComplete();
2341 anotherInput.setParentItem(view.rootObject());
2342 anotherInput.setFocus(true);
2343 QCOMPARE(qApp->inputPanel()->visible(), true);
2344 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2345 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2347 anotherInput.setFocus(false);
2348 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2349 QCOMPARE(view.activeFocusItem(), view.rootItem());
2350 anotherInput.setFocus(true);
2352 // input item should be null if focus is lost to an item that doesn't accept inputs
2354 item.setParentItem(view.rootObject());
2355 item.setFocus(true);
2356 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2357 QCOMPARE(view.activeFocusItem(), &item);
2359 qApp->inputPanel()->hide();
2361 // input panel should not be opened if TextInput is read only
2362 input->setReadOnly(true);
2363 input->setFocus(true);
2364 QCOMPARE(qApp->inputPanel()->visible(), false);
2365 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2366 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2367 QGuiApplication::processEvents();
2368 QCOMPARE(qApp->inputPanel()->visible(), false);
2370 // input panel should not be opened if focusOnPress is set to false
2371 input->setFocusOnPress(false);
2372 input->setFocus(false);
2373 input->setFocus(true);
2374 QCOMPARE(qApp->inputPanel()->visible(), false);
2375 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2376 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2377 QCOMPARE(qApp->inputPanel()->visible(), false);
2379 // input panel should open when openSoftwareInputPanel is called
2380 input->openSoftwareInputPanel();
2381 QCOMPARE(qApp->inputPanel()->visible(), true);
2383 // input panel should close when closeSoftwareInputPanel is called
2384 input->closeSoftwareInputPanel();
2385 QCOMPARE(qApp->inputPanel()->visible(), false);
2388 class MyTextInput : public QQuickTextInput
2391 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2395 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2398 return QQuickTextInput::updatePaintNode(node, data);
2403 void tst_qquicktextinput::setHAlignClearCache()
2407 input.setText("Hello world");
2408 input.setParentItem(view.rootItem());
2410 view.requestActivateWindow();
2411 QTest::qWaitForWindowShown(&view);
2412 QTRY_COMPARE(input.nbPaint, 1);
2413 input.setHAlign(QQuickTextInput::AlignRight);
2414 //Changing the alignment should trigger a repaint
2415 QTRY_COMPARE(input.nbPaint, 2);
2418 void tst_qquicktextinput::focusOutClearSelection()
2421 QQuickTextInput input;
2422 QQuickTextInput input2;
2423 input.setText(QLatin1String("Hello world"));
2424 input.setFocus(true);
2425 input2.setParentItem(view.rootItem());
2426 input.setParentItem(view.rootItem());
2427 input.componentComplete();
2428 input2.componentComplete();
2430 view.requestActivateWindow();
2431 QTest::qWaitForWindowShown(&view);
2433 //The selection should work
2434 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2435 input2.setFocus(true);
2436 QGuiApplication::processEvents();
2437 //The input lost the focus selection should be cleared
2438 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2441 void tst_qquicktextinput::geometrySignals()
2443 QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2444 QObject *o = component.create();
2446 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2447 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2451 void tst_qquicktextinput::testQtQuick11Attributes()
2453 QFETCH(QString, code);
2454 QFETCH(QString, warning);
2455 QFETCH(QString, error);
2457 QDeclarativeEngine engine;
2460 QDeclarativeComponent valid(&engine);
2461 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2462 obj = valid.create();
2464 QVERIFY(valid.errorString().isEmpty());
2467 QDeclarativeComponent invalid(&engine);
2468 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2469 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2470 obj = invalid.create();
2471 QCOMPARE(invalid.errorString(), error);
2475 void tst_qquicktextinput::testQtQuick11Attributes_data()
2477 QTest::addColumn<QString>("code");
2478 QTest::addColumn<QString>("warning");
2479 QTest::addColumn<QString>("error");
2481 QTest::newRow("canPaste") << "property bool foo: canPaste"
2482 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2485 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2486 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2489 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2490 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2494 static void sendPreeditText(const QString &text, int cursor)
2496 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2497 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2498 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2501 void tst_qquicktextinput::preeditAutoScroll()
2503 QString preeditText = "califragisiticexpialidocious!";
2505 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2507 view.requestActivateWindow();
2508 QTest::qWaitForWindowShown(&view);
2509 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2510 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2512 QVERIFY(input->hasActiveFocus());
2514 input->setWidth(input->implicitWidth());
2516 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2517 int cursorRectangleChanges = 0;
2519 // test the text is scrolled so the preedit is visible.
2520 sendPreeditText(preeditText.mid(0, 3), 1);
2521 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2522 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2523 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2525 // test the text is scrolled back when the preedit is removed.
2526 QInputMethodEvent imEvent;
2527 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2528 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2529 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2530 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2532 QTextLayout layout(preeditText);
2533 layout.setFont(input->font());
2534 if (!qmlDisableDistanceField()) {
2536 option.setUseDesignMetrics(true);
2537 layout.setTextOption(option);
2539 layout.beginLayout();
2540 QTextLine line = layout.createLine();
2543 // test if the preedit is larger than the text input that the
2544 // character preceding the cursor is still visible.
2545 qreal x = input->positionToRectangle(0).x();
2546 for (int i = 0; i < 3; ++i) {
2547 sendPreeditText(preeditText, i + 1);
2548 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2549 QVERIFY(input->cursorRectangle().right() >= width - 3);
2550 QVERIFY(input->positionToRectangle(0).x() < x);
2551 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2552 x = input->positionToRectangle(0).x();
2554 for (int i = 1; i >= 0; --i) {
2555 sendPreeditText(preeditText, i + 1);
2556 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2557 QVERIFY(input->cursorRectangle().right() >= width - 3);
2558 QVERIFY(input->positionToRectangle(0).x() > x);
2559 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2560 x = input->positionToRectangle(0).x();
2563 // Test incrementing the preedit cursor doesn't cause further
2564 // scrolling when right most text is visible.
2565 sendPreeditText(preeditText, preeditText.length() - 3);
2566 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2567 x = input->positionToRectangle(0).x();
2568 for (int i = 2; i >= 0; --i) {
2569 sendPreeditText(preeditText, preeditText.length() - i);
2570 QCOMPARE(input->positionToRectangle(0).x(), x);
2571 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2573 for (int i = 1; i < 3; ++i) {
2574 sendPreeditText(preeditText, preeditText.length() - i);
2575 QCOMPARE(input->positionToRectangle(0).x(), x);
2576 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2579 // Test disabling auto scroll.
2580 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2582 input->setAutoScroll(false);
2583 sendPreeditText(preeditText.mid(0, 3), 1);
2584 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2585 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2588 void tst_qquicktextinput::preeditCursorRectangle()
2590 QString preeditText = "super";
2592 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2594 view.requestActivateWindow();
2595 QTest::qWaitForWindowShown(&view);
2596 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2597 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2602 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2603 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2604 QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2606 // Verify that the micro focus rect is positioned the same for position 0 as
2607 // it would be if there was no preedit text.
2608 sendPreeditText(preeditText, 0);
2609 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2610 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2611 QCOMPARE(currentRect, previousRect);
2613 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2614 QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2616 // Verify that the micro focus rect moves to the left as the cursor position
2618 for (int i = 1; i <= 5; ++i) {
2619 sendPreeditText(preeditText, i);
2620 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2621 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2622 QVERIFY(previousRect.left() < currentRect.left());
2623 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2624 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2625 previousRect = currentRect;
2628 // Verify that if there is no preedit cursor then the micro focus rect is the
2629 // same as it would be if it were positioned at the end of the preedit text.
2630 sendPreeditText(preeditText, 0);
2631 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2632 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2633 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2634 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2635 QCOMPARE(currentRect, previousRect);
2636 QVERIFY(inputSpy.count() > 0);
2637 QVERIFY(panelSpy.count() > 0);
2640 void tst_qquicktextinput::inputContextMouseHandler()
2642 PlatformInputContext platformInputContext;
2643 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2644 inputPanelPrivate->testContext = &platformInputContext;
2646 QString text = "supercalifragisiticexpialidocious!";
2647 QQuickView view(testFileUrl("inputContext.qml"));
2648 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2651 input->setFocus(true);
2655 view.requestActivateWindow();
2656 QTest::qWaitForWindowShown(&view);
2657 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2659 QTextLayout layout(text);
2660 layout.setFont(input->font());
2661 if (!qmlDisableDistanceField()) {
2663 option.setUseDesignMetrics(true);
2664 layout.setTextOption(option);
2666 layout.beginLayout();
2667 QTextLine line = layout.createLine();
2670 const qreal x = line.cursorToX(2, QTextLine::Leading);
2671 const qreal y = line.height() / 2;
2672 QPoint position = QPointF(x, y).toPoint();
2674 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2675 QApplication::sendEvent(input, &inputEvent);
2677 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2678 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2679 QGuiApplication::processEvents();
2681 QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2682 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2683 QCOMPARE(platformInputContext.m_cursorPosition, 2);
2686 void tst_qquicktextinput::inputMethodComposing()
2688 QString text = "supercalifragisiticexpialidocious!";
2690 QQuickView view(testFileUrl("inputContext.qml"));
2692 view.requestActivateWindow();
2693 QTest::qWaitForWindowShown(&view);
2694 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2695 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2697 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2699 QCOMPARE(input->isInputMethodComposing(), false);
2701 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2702 QGuiApplication::sendEvent(input, &event);
2704 QCOMPARE(input->isInputMethodComposing(), true);
2705 QCOMPARE(spy.count(), 1);
2708 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2709 QGuiApplication::sendEvent(input, &event);
2711 QCOMPARE(spy.count(), 1);
2714 QInputMethodEvent event;
2715 QGuiApplication::sendEvent(input, &event);
2717 QCOMPARE(input->isInputMethodComposing(), false);
2718 QCOMPARE(spy.count(), 2);
2721 void tst_qquicktextinput::cursorRectangleSize()
2723 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
2724 QVERIFY(canvas->rootObject() != 0);
2725 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2727 // make sure cursor rectangle is not at (0,0)
2728 textInput->setX(10);
2729 textInput->setY(10);
2730 textInput->setCursorPosition(3);
2731 QVERIFY(textInput != 0);
2732 textInput->setFocus(true);
2734 canvas->requestActivateWindow();
2735 QTest::qWaitForWindowShown(canvas);
2737 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2738 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2739 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
2741 QRect cursorRectFromItem = textInput->cursorRectangle();
2742 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
2744 // item and input query cursor rectangles match
2745 QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
2747 // item cursor rectangle and positionToRectangle calculations match
2748 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
2750 // item-canvas transform and input item transform match
2752 QEXPECT_FAIL("","QTBUG-22966", Abort);
2754 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
2756 // input panel cursorRectangle property and tranformed item cursor rectangle match
2757 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
2758 QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
2763 void tst_qquicktextinput::tripleClickSelectsAll()
2765 QString qmlfile = testFile("positionAt.qml");
2766 QQuickView view(QUrl::fromLocalFile(qmlfile));
2768 view.requestActivateWindow();
2769 QTest::qWaitForWindowShown(&view);
2771 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2773 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2776 QLatin1String hello("Hello world!");
2777 input->setSelectByMouse(true);
2778 input->setText(hello);
2780 // Clicking on the same point inside TextInput three times in a row
2781 // should trigger a triple click, thus selecting all the text.
2782 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2783 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2784 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2785 QGuiApplication::processEvents();
2786 QCOMPARE(input->selectedText(), hello);
2788 // Now it simulates user moving the mouse between the second and the third click.
2789 // In this situation, we don't expect a triple click.
2790 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2791 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2792 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2793 QGuiApplication::processEvents();
2794 QVERIFY(input->selectedText().isEmpty());
2796 // And now we press the third click too late, so no triple click event is triggered.
2797 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2798 QGuiApplication::processEvents();
2799 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
2800 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2801 QGuiApplication::processEvents();
2802 QVERIFY(input->selectedText().isEmpty());
2805 void tst_qquicktextinput::QTBUG_19956_data()
2807 QTest::addColumn<QString>("url");
2808 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2809 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2812 void tst_qquicktextinput::keySequence_data()
2814 QTest::addColumn<QString>("text");
2815 QTest::addColumn<QKeySequence>("sequence");
2816 QTest::addColumn<int>("selectionStart");
2817 QTest::addColumn<int>("selectionEnd");
2818 QTest::addColumn<int>("cursorPosition");
2819 QTest::addColumn<QString>("expectedText");
2820 QTest::addColumn<QString>("selectedText");
2822 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
2824 QTest::newRow("select all")
2825 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
2826 << 44 << standard.at(0) << standard.at(0);
2827 QTest::newRow("select end of line")
2828 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
2829 << 44 << standard.at(0) << standard.at(0).mid(5);
2830 QTest::newRow("select end of document") // ### Not handled.
2831 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
2832 << 3 << standard.at(0) << QString();
2833 QTest::newRow("select end of block")
2834 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
2835 << 44 << standard.at(0) << standard.at(0).mid(18);
2836 QTest::newRow("delete end of line")
2837 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
2838 << 24 << standard.at(0).mid(0, 24) << QString();
2839 QTest::newRow("move to start of line")
2840 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
2841 << 0 << standard.at(0) << QString();
2842 QTest::newRow("move to start of block")
2843 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
2844 << 0 << standard.at(0) << QString();
2845 QTest::newRow("move to next char")
2846 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
2847 << 13 << standard.at(0) << QString();
2848 QTest::newRow("move to previous char")
2849 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
2850 << 2 << standard.at(0) << QString();
2851 QTest::newRow("select next char")
2852 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
2853 << 24 << standard.at(0) << standard.at(0).mid(23, 1);
2854 QTest::newRow("select previous char")
2855 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
2856 << 18 << standard.at(0) << standard.at(0).mid(18, 1);
2857 QTest::newRow("move to next word")
2858 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
2859 << 10 << standard.at(0) << QString();
2860 QTest::newRow("move to previous word")
2861 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
2862 << 4 << standard.at(0) << QString();
2863 QTest::newRow("select previous word")
2864 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
2865 << 10 << standard.at(0) << standard.at(0).mid(10, 1);
2866 QTest::newRow("delete (selection)")
2867 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
2868 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
2869 QTest::newRow("delete (no selection)")
2870 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
2871 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
2872 QTest::newRow("delete end of word")
2873 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
2874 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
2875 QTest::newRow("delete start of word")
2876 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
2877 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
2880 void tst_qquicktextinput::keySequence()
2882 QFETCH(QString, text);
2883 QFETCH(QKeySequence, sequence);
2884 QFETCH(int, selectionStart);
2885 QFETCH(int, selectionEnd);
2886 QFETCH(int, cursorPosition);
2887 QFETCH(QString, expectedText);
2888 QFETCH(QString, selectedText);
2890 if (sequence.isEmpty()) {
2891 QSKIP("Key sequence is undefined");
2894 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
2895 QDeclarativeComponent textInputComponent(&engine);
2896 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2897 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2898 QVERIFY(textInput != 0);
2900 QQuickCanvas canvas;
2901 textInput->setParentItem(canvas.rootItem());
2903 canvas.requestActivateWindow();
2904 QTest::qWaitForWindowShown(&canvas);
2905 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2907 textInput->select(selectionStart, selectionEnd);
2909 simulateKeys(&canvas, sequence);
2911 QCOMPARE(textInput->cursorPosition(), cursorPosition);
2912 QCOMPARE(textInput->text(), expectedText);
2913 QCOMPARE(textInput->selectedText(), selectedText);
2917 #define REPLACE_UNTIL_END 1
2919 void tst_qquicktextinput::undo_data()
2921 QTest::addColumn<QStringList>("insertString");
2922 QTest::addColumn<IntList>("insertIndex");
2923 QTest::addColumn<IntList>("insertMode");
2924 QTest::addColumn<QStringList>("expectedString");
2925 QTest::addColumn<bool>("use_keys");
2927 for (int i=0; i<2; i++) {
2928 QString keys_str = "keyboard";
2929 bool use_keys = true;
2931 keys_str = "insert";
2936 IntList insertIndex;
2938 QStringList insertString;
2939 QStringList expectedString;
2942 insertMode << NORMAL;
2943 insertString << "1";
2946 insertMode << NORMAL;
2947 insertString << "5";
2950 insertMode << NORMAL;
2951 insertString << "3";
2954 insertMode << NORMAL;
2955 insertString << "2";
2958 insertMode << NORMAL;
2959 insertString << "4";
2961 expectedString << "12345";
2962 expectedString << "1235";
2963 expectedString << "135";
2964 expectedString << "15";
2965 expectedString << "";
2967 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
2975 IntList insertIndex;
2977 QStringList insertString;
2978 QStringList expectedString;
2981 insertMode << NORMAL;
2982 insertString << "World"; // World
2985 insertMode << NORMAL;
2986 insertString << "Hello"; // HelloWorld
2989 insertMode << NORMAL;
2990 insertString << "Well"; // WellHelloWorld
2993 insertMode << NORMAL;
2994 insertString << "There"; // WellHelloThereWorld;
2996 expectedString << "WellHelloThereWorld";
2997 expectedString << "WellHelloWorld";
2998 expectedString << "HelloWorld";
2999 expectedString << "World";
3000 expectedString << "";
3002 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
3010 IntList insertIndex;
3012 QStringList insertString;
3013 QStringList expectedString;
3016 insertMode << NORMAL;
3017 insertString << "Ensuring";
3020 insertMode << NORMAL;
3021 insertString << " instan";
3024 insertMode << NORMAL;
3025 insertString << "an ";
3028 insertMode << REPLACE_UNTIL_END;
3029 insertString << " unique instance.";
3031 expectedString << "Ensuring a unique instance.";
3032 expectedString << "Ensuring an instan";
3033 expectedString << "Ensuring instan";
3034 expectedString << "";
3036 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
3046 void tst_qquicktextinput::undo()
3048 QFETCH(QStringList, insertString);
3049 QFETCH(IntList, insertIndex);
3050 QFETCH(IntList, insertMode);
3051 QFETCH(QStringList, expectedString);
3053 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3054 QDeclarativeComponent textInputComponent(&engine);
3055 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3056 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3057 QVERIFY(textInput != 0);
3059 QQuickCanvas canvas;
3060 textInput->setParentItem(canvas.rootItem());
3062 canvas.requestActivateWindow();
3063 QTest::qWaitForWindowShown(&canvas);
3064 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3068 // STEP 1: First build up an undo history by inserting or typing some strings...
3069 for (i = 0; i < insertString.size(); ++i) {
3070 if (insertIndex[i] > -1)
3071 textInput->setCursorPosition(insertIndex[i]);
3073 // experimental stuff
3074 if (insertMode[i] == REPLACE_UNTIL_END) {
3075 textInput->select(insertIndex[i], insertIndex[i] + 8);
3077 // This is what I actually want...
3078 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
3081 for (int j = 0; j < insertString.at(i).length(); j++)
3082 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3085 // STEP 2: Next call undo several times and see if we can restore to the previous state
3086 for (i = 0; i < expectedString.size() - 1; ++i) {
3087 QCOMPARE(textInput->text(), expectedString[i]);
3088 simulateKeys(&canvas, QKeySequence::Undo);
3091 // STEP 3: Verify that we have undone everything
3092 QVERIFY(textInput->text().isEmpty());
3095 void tst_qquicktextinput::redo_data()
3097 QTest::addColumn<QStringList>("insertString");
3098 QTest::addColumn<IntList>("insertIndex");
3099 QTest::addColumn<QStringList>("expectedString");
3102 IntList insertIndex;
3103 QStringList insertString;
3104 QStringList expectedString;
3107 insertString << "World"; // World
3109 insertString << "Hello"; // HelloWorld
3111 insertString << "Well"; // WellHelloWorld
3113 insertString << "There"; // WellHelloThereWorld;
3115 expectedString << "World";
3116 expectedString << "HelloWorld";
3117 expectedString << "WellHelloWorld";
3118 expectedString << "WellHelloThereWorld";
3120 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
3124 void tst_qquicktextinput::redo()
3126 QFETCH(QStringList, insertString);
3127 QFETCH(IntList, insertIndex);
3128 QFETCH(QStringList, expectedString);
3130 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3131 QDeclarativeComponent textInputComponent(&engine);
3132 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3133 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3134 QVERIFY(textInput != 0);
3136 QQuickCanvas canvas;
3137 textInput->setParentItem(canvas.rootItem());
3139 canvas.requestActivateWindow();
3140 QTest::qWaitForWindowShown(&canvas);
3141 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3144 // inserts the diff strings at diff positions
3145 for (i = 0; i < insertString.size(); ++i) {
3146 if (insertIndex[i] > -1)
3147 textInput->setCursorPosition(insertIndex[i]);
3148 for (int j = 0; j < insertString.at(i).length(); j++)
3149 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3153 while (!textInput->text().isEmpty())
3154 simulateKeys(&canvas, QKeySequence::Undo);
3156 for (i = 0; i < expectedString.size(); ++i) {
3157 simulateKeys(&canvas, QKeySequence::Redo);
3158 QCOMPARE(textInput->text() , expectedString[i]);
3162 void tst_qquicktextinput::undo_keypressevents_data()
3164 QTest::addColumn<KeyList>("keys");
3165 QTest::addColumn<QStringList>("expectedString");
3169 QStringList expectedString;
3172 << QKeySequence::MoveToStartOfLine
3179 << QKeySequence::MoveToEndOfLine
3182 expectedString << "BEVERYAFRAID!";
3183 expectedString << "BEVERYAFRAID";
3184 expectedString << "VERYAFRAID";
3185 expectedString << "AFRAID";
3187 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
3190 QStringList expectedString;
3193 keys << "1234" << QKeySequence::MoveToStartOfLine
3195 << Qt::Key_Right << Qt::Key_Right
3197 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3201 expectedString << "12";
3202 expectedString << "1234";
3204 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
3207 QStringList expectedString;
3211 << QKeySequence::MoveToStartOfLine
3213 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3215 << QKeySequence::Undo
3217 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
3220 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3223 expectedString << "AB";
3224 expectedString << "AB12";
3226 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
3229 QStringList expectedString;
3234 << Qt::Key_Left << Qt::Key_Left
3238 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3239 // overwriting '1234' with '5'
3241 // undoing deletion of 'AB'
3242 << QKeySequence::Undo
3243 // overwriting '1234' with '6'
3246 expectedString << "ab6cd";
3247 // for versions previous to 3.2 we overwrite needed two undo operations
3248 expectedString << "ab1234cd";
3249 expectedString << "abcd";
3251 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
3254 QStringList expectedString;
3259 << Qt::Key_Backspace;
3261 expectedString << "AB";
3262 expectedString << "ABC";
3264 QTest::newRow("Inserts,backspace") << keys << expectedString;
3267 QStringList expectedString;
3271 << Qt::Key_Backspace
3275 expectedString << "ABZ";
3276 expectedString << "AB";
3277 expectedString << "ABC";
3279 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
3282 QStringList expectedString;
3285 keys << "123" << QKeySequence::MoveToStartOfLine
3287 << QKeySequence::SelectEndOfLine
3288 // overwriting '123' with 'ABC'
3291 expectedString << "ABC";
3292 // for versions previous to 3.2 we overwrite needed two undo operations
3293 expectedString << "123";
3295 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
3299 void tst_qquicktextinput::undo_keypressevents()
3301 QFETCH(KeyList, keys);
3302 QFETCH(QStringList, expectedString);
3304 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3305 QDeclarativeComponent textInputComponent(&engine);
3306 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3307 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3308 QVERIFY(textInput != 0);
3310 QQuickCanvas canvas;
3311 textInput->setParentItem(canvas.rootItem());
3313 canvas.requestActivateWindow();
3314 QTest::qWaitForWindowShown(&canvas);
3315 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3317 simulateKeys(&canvas, keys);
3319 for (int i = 0; i < expectedString.size(); ++i) {
3320 QCOMPARE(textInput->text() , expectedString[i]);
3321 simulateKeys(&canvas, QKeySequence::Undo);
3323 QVERIFY(textInput->text().isEmpty());
3326 void tst_qquicktextinput::QTBUG_19956()
3328 QFETCH(QString, url);
3330 QQuickView canvas(testFileUrl(url));
3332 canvas.requestActivateWindow();
3333 QTest::qWaitForWindowShown(&canvas);
3334 QVERIFY(canvas.rootObject() != 0);
3335 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
3337 input->setFocus(true);
3338 QVERIFY(input->hasActiveFocus());
3340 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
3341 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
3342 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
3343 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3345 canvas.rootObject()->setProperty("topvalue", 15);
3346 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
3347 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3349 canvas.rootObject()->setProperty("topvalue", 25);
3350 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
3351 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3353 canvas.rootObject()->setProperty("bottomvalue", 21);
3354 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
3355 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3357 canvas.rootObject()->setProperty("bottomvalue", 10);
3358 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
3359 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3362 void tst_qquicktextinput::QTBUG_19956_regexp()
3364 QUrl url = testFileUrl("qtbug-19956regexp.qml");
3366 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
3367 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
3369 QQuickView canvas(url);
3371 canvas.requestActivateWindow();
3372 QTest::qWaitForWindowShown(&canvas);
3373 QVERIFY(canvas.rootObject() != 0);
3374 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
3376 input->setFocus(true);
3377 QVERIFY(input->hasActiveFocus());
3379 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
3380 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
3381 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
3382 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3384 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
3385 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
3386 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3388 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
3389 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
3390 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3393 QTEST_MAIN(tst_qquicktextinput)
3395 #include "tst_qquicktextinput.moc"