1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/util.h"
44 #include <private/qinputpanel_p.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputPanel>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
57 #include <QtOpenGL/QGLShaderProgram>
61 #include <Carbon/Carbon.h>
64 #include "qplatformdefs.h"
65 #include "../../shared/platforminputcontext.h"
67 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
68 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
70 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
72 // XXX This will be replaced by some clever persistent platform image store.
73 QString persistent_dir = QDeclarativeDataTest::instance()->dataDirectory();
74 QString arch = "unknown-architecture"; // QTest needs to help with this.
76 QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
78 if (!QFile::exists(expectfile)) {
79 actual.save(expectfile);
80 qWarning() << "created" << expectfile;
86 template <typename T> static T evaluate(QObject *scope, const QString &expression)
88 QDeclarativeExpression expr(qmlContext(scope), scope, expression);
89 T result = expr.evaluate().value<T>();
91 qWarning() << expr.error().toString();
95 typedef QPair<int, QChar> Key;
97 class tst_qquicktextinput : public QDeclarativeDataTest
102 tst_qquicktextinput();
112 void isRightToLeft_data();
113 void isRightToLeft();
114 void moveCursorSelection_data();
115 void moveCursorSelection();
116 void moveCursorSelectionSequence_data();
117 void moveCursorSelectionSequence();
118 void dragMouseSelection();
119 void mouseSelectionMode_data();
120 void mouseSelectionMode();
121 void tripleClickSelectsAll();
123 void horizontalAlignment_data();
124 void horizontalAlignment();
125 void horizontalAlignment_RightToLeft();
126 void verticalAlignment();
137 void passwordCharacter();
138 void cursorDelegate();
139 void cursorVisible();
140 void cursorRectangle();
142 void navigation_RTL();
144 void copyAndPasteKeySequence();
145 void canPasteEmpty();
149 void openInputPanel();
150 void setHAlignClearCache();
151 void focusOutClearSelection();
154 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
155 void passwordEchoDelay();
157 void geometrySignals();
158 void testQtQuick11Attributes();
159 void testQtQuick11Attributes_data();
161 void preeditAutoScroll();
162 void preeditCursorRectangle();
163 void inputContextMouseHandler();
164 void inputMethodComposing();
165 void inputPanelUpdate();
166 void cursorRectangleSize();
175 void keySequence_data();
182 void undo_keypressevents_data();
183 void undo_keypressevents();
186 void QTBUG_19956_data();
187 void QTBUG_19956_regexp();
189 void negativeDimensions();
192 void simulateKey(QQuickView *, int key);
194 void simulateKeys(QWindow *window, const QList<Key> &keys);
195 void simulateKeys(QWindow *window, const QKeySequence &sequence);
197 QDeclarativeEngine engine;
198 QStringList standard;
199 QStringList colorStrings;
202 typedef QList<int> IntList;
203 Q_DECLARE_METATYPE(IntList)
205 typedef QList<Key> KeyList;
206 Q_DECLARE_METATYPE(KeyList)
208 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
210 for (int i = 0; i < keys.count(); ++i) {
211 const int key = keys.at(i).first;
212 const int modifiers = key & Qt::KeyboardModifierMask;
213 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
215 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
216 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
218 QGuiApplication::sendEvent(window, &press);
219 QGuiApplication::sendEvent(window, &release);
223 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
225 for (uint i = 0; i < sequence.count(); ++i) {
226 const int key = sequence[i];
227 const int modifiers = key & Qt::KeyboardModifierMask;
229 QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
233 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
235 for (uint i = 0; i < sequence.count(); ++i)
236 keys << Key(sequence[i], QChar());
240 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
242 for (int i = 0; i < N - 1; ++i) {
243 int key = QTest::asciiToKey(characters[i]);
244 QChar character = QLatin1Char(characters[i]);
245 keys << Key(key, character);
250 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
252 keys << Key(key, QChar());
256 void tst_qquicktextinput::cleanup()
258 // ensure not even skipped tests with custom input context leave it dangling
259 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
260 inputPanelPrivate->testContext = 0;
263 tst_qquicktextinput::tst_qquicktextinput()
265 standard << "the quick brown fox jumped over the lazy dog"
266 << "It's supercalifragisiticexpialidocious!"
271 colorStrings << "aliceblue"
285 void tst_qquicktextinput::text()
288 QDeclarativeComponent textinputComponent(&engine);
289 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
290 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
292 QVERIFY(textinputObject != 0);
293 QCOMPARE(textinputObject->text(), QString(""));
294 QCOMPARE(textinputObject->length(), 0);
296 delete textinputObject;
299 for (int i = 0; i < standard.size(); i++)
301 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
302 QDeclarativeComponent textinputComponent(&engine);
303 textinputComponent.setData(componentStr.toLatin1(), QUrl());
304 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
306 QVERIFY(textinputObject != 0);
307 QCOMPARE(textinputObject->text(), standard.at(i));
308 QCOMPARE(textinputObject->length(), standard.at(i).length());
310 delete textinputObject;
315 void tst_qquicktextinput::width()
317 // uses Font metrics to find the width for standard
319 QDeclarativeComponent textinputComponent(&engine);
320 textinputComponent.setData("import QtQuick 2.0\nTextInput { text: \"\" }", QUrl());
321 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
323 QVERIFY(textinputObject != 0);
324 QCOMPARE(textinputObject->width(), 0.0);
326 delete textinputObject;
329 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
331 for (int i = 0; i < standard.size(); i++)
333 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
334 QDeclarativeComponent textinputComponent(&engine);
335 textinputComponent.setData(componentStr.toLatin1(), QUrl());
336 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
338 QString s = standard.at(i);
339 s.replace(QLatin1Char('\n'), QChar::LineSeparator);
341 QTextLayout layout(s);
342 layout.setFont(textinputObject->font());
343 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
344 if (requiresUnhintedMetrics) {
346 option.setUseDesignMetrics(true);
347 layout.setTextOption(option);
350 layout.beginLayout();
352 QTextLine line = layout.createLine();
359 qreal metricWidth = ceil(layout.boundingRect().width());
361 QVERIFY(textinputObject != 0);
362 int delta = abs(int(int(textinputObject->width()) - metricWidth));
363 QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
365 delete textinputObject;
369 void tst_qquicktextinput::font()
371 //test size, then bold, then italic, then family
373 QString componentStr = "import QtQuick 2.0\nTextInput { font.pointSize: 40; text: \"Hello World\" }";
374 QDeclarativeComponent textinputComponent(&engine);
375 textinputComponent.setData(componentStr.toLatin1(), QUrl());
376 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
378 QVERIFY(textinputObject != 0);
379 QCOMPARE(textinputObject->font().pointSize(), 40);
380 QCOMPARE(textinputObject->font().bold(), false);
381 QCOMPARE(textinputObject->font().italic(), false);
383 delete textinputObject;
387 QString componentStr = "import QtQuick 2.0\nTextInput { font.bold: true; text: \"Hello World\" }";
388 QDeclarativeComponent textinputComponent(&engine);
389 textinputComponent.setData(componentStr.toLatin1(), QUrl());
390 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
392 QVERIFY(textinputObject != 0);
393 QCOMPARE(textinputObject->font().bold(), true);
394 QCOMPARE(textinputObject->font().italic(), false);
396 delete textinputObject;
400 QString componentStr = "import QtQuick 2.0\nTextInput { font.italic: true; text: \"Hello World\" }";
401 QDeclarativeComponent textinputComponent(&engine);
402 textinputComponent.setData(componentStr.toLatin1(), QUrl());
403 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
405 QVERIFY(textinputObject != 0);
406 QCOMPARE(textinputObject->font().italic(), true);
407 QCOMPARE(textinputObject->font().bold(), false);
409 delete textinputObject;
413 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"Helvetica\"; text: \"Hello World\" }";
414 QDeclarativeComponent textinputComponent(&engine);
415 textinputComponent.setData(componentStr.toLatin1(), QUrl());
416 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
418 QVERIFY(textinputObject != 0);
419 QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
420 QCOMPARE(textinputObject->font().bold(), false);
421 QCOMPARE(textinputObject->font().italic(), false);
423 delete textinputObject;
427 QString componentStr = "import QtQuick 2.0\nTextInput { font.family: \"\"; text: \"Hello World\" }";
428 QDeclarativeComponent textinputComponent(&engine);
429 textinputComponent.setData(componentStr.toLatin1(), QUrl());
430 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
432 QVERIFY(textinputObject != 0);
433 QCOMPARE(textinputObject->font().family(), QString(""));
435 delete textinputObject;
439 void tst_qquicktextinput::color()
442 for (int i = 0; i < colorStrings.size(); i++)
444 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
445 QDeclarativeComponent textinputComponent(&engine);
446 textinputComponent.setData(componentStr.toLatin1(), QUrl());
447 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
448 QVERIFY(textinputObject != 0);
449 QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
451 delete textinputObject;
454 //test selection color
455 for (int i = 0; i < colorStrings.size(); i++)
457 QString componentStr = "import QtQuick 2.0\nTextInput { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
458 QDeclarativeComponent textinputComponent(&engine);
459 textinputComponent.setData(componentStr.toLatin1(), QUrl());
460 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
461 QVERIFY(textinputObject != 0);
462 QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
464 delete textinputObject;
467 //test selected text color
468 for (int i = 0; i < colorStrings.size(); i++)
470 QString componentStr = "import QtQuick 2.0\nTextInput { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
471 QDeclarativeComponent textinputComponent(&engine);
472 textinputComponent.setData(componentStr.toLatin1(), QUrl());
473 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
474 QVERIFY(textinputObject != 0);
475 QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
477 delete textinputObject;
481 QString colorStr = "#AA001234";
482 QColor testColor("#001234");
483 testColor.setAlpha(170);
485 QString componentStr = "import QtQuick 2.0\nTextInput { color: \"" + colorStr + "\"; text: \"Hello World\" }";
486 QDeclarativeComponent textinputComponent(&engine);
487 textinputComponent.setData(componentStr.toLatin1(), QUrl());
488 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
490 QVERIFY(textinputObject != 0);
491 QCOMPARE(textinputObject->color(), testColor);
493 delete textinputObject;
497 void tst_qquicktextinput::wrap()
500 // for specified width and wrap set true
502 QDeclarativeComponent textComponent(&engine);
503 textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
504 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
505 textHeight = textObject->height();
507 QVERIFY(textObject != 0);
508 QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
509 QCOMPARE(textObject->width(), 300.);
514 for (int i = 0; i < standard.count(); i++) {
515 QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
516 QDeclarativeComponent textComponent(&engine);
517 textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
518 QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
520 QVERIFY(textObject != 0);
521 QCOMPARE(textObject->width(), 30.);
522 QVERIFY(textObject->height() > textHeight);
524 int oldHeight = textObject->height();
525 textObject->setWidth(100);
526 QVERIFY(textObject->height() < oldHeight);
532 void tst_qquicktextinput::selection()
534 QString testStr = standard[0];
535 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
536 QDeclarativeComponent textinputComponent(&engine);
537 textinputComponent.setData(componentStr.toLatin1(), QUrl());
538 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
539 QVERIFY(textinputObject != 0);
542 //Test selection follows cursor
543 for (int i=0; i<= testStr.size(); i++) {
544 textinputObject->setCursorPosition(i);
545 QCOMPARE(textinputObject->cursorPosition(), i);
546 QCOMPARE(textinputObject->selectionStart(), i);
547 QCOMPARE(textinputObject->selectionEnd(), i);
548 QVERIFY(textinputObject->selectedText().isNull());
551 textinputObject->setCursorPosition(0);
552 QVERIFY(textinputObject->cursorPosition() == 0);
553 QVERIFY(textinputObject->selectionStart() == 0);
554 QVERIFY(textinputObject->selectionEnd() == 0);
555 QVERIFY(textinputObject->selectedText().isNull());
557 // Verify invalid positions are ignored.
558 textinputObject->setCursorPosition(-1);
559 QVERIFY(textinputObject->cursorPosition() == 0);
560 QVERIFY(textinputObject->selectionStart() == 0);
561 QVERIFY(textinputObject->selectionEnd() == 0);
562 QVERIFY(textinputObject->selectedText().isNull());
564 textinputObject->setCursorPosition(textinputObject->text().count()+1);
565 QVERIFY(textinputObject->cursorPosition() == 0);
566 QVERIFY(textinputObject->selectionStart() == 0);
567 QVERIFY(textinputObject->selectionEnd() == 0);
568 QVERIFY(textinputObject->selectedText().isNull());
571 for (int i=0; i<= testStr.size(); i++) {
572 textinputObject->select(0,i);
573 QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
575 for (int i=0; i<= testStr.size(); i++) {
576 textinputObject->select(i,testStr.size());
577 QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
580 textinputObject->setCursorPosition(0);
581 QVERIFY(textinputObject->cursorPosition() == 0);
582 QVERIFY(textinputObject->selectionStart() == 0);
583 QVERIFY(textinputObject->selectionEnd() == 0);
584 QVERIFY(textinputObject->selectedText().isNull());
586 //Test Error Ignoring behaviour
587 textinputObject->setCursorPosition(0);
588 QVERIFY(textinputObject->selectedText().isNull());
589 textinputObject->select(-10,0);
590 QVERIFY(textinputObject->selectedText().isNull());
591 textinputObject->select(100,110);
592 QVERIFY(textinputObject->selectedText().isNull());
593 textinputObject->select(0,-10);
594 QVERIFY(textinputObject->selectedText().isNull());
595 textinputObject->select(0,100);
596 QVERIFY(textinputObject->selectedText().isNull());
597 textinputObject->select(0,10);
598 QVERIFY(textinputObject->selectedText().size() == 10);
599 textinputObject->select(-10,10);
600 QVERIFY(textinputObject->selectedText().size() == 10);
601 textinputObject->select(100,101);
602 QVERIFY(textinputObject->selectedText().size() == 10);
603 textinputObject->select(0,-10);
604 QVERIFY(textinputObject->selectedText().size() == 10);
605 textinputObject->select(0,100);
606 QVERIFY(textinputObject->selectedText().size() == 10);
608 textinputObject->deselect();
609 QVERIFY(textinputObject->selectedText().isNull());
610 textinputObject->select(0,10);
611 QVERIFY(textinputObject->selectedText().size() == 10);
612 textinputObject->deselect();
613 QVERIFY(textinputObject->selectedText().isNull());
615 // test input method selection
616 QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
617 textinputObject->setFocus(true);
619 QList<QInputMethodEvent::Attribute> attributes;
620 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
621 QInputMethodEvent event("", attributes);
622 QGuiApplication::sendEvent(textinputObject, &event);
624 QCOMPARE(selectionSpy.count(), 1);
625 QCOMPARE(textinputObject->selectionStart(), 12);
626 QCOMPARE(textinputObject->selectionEnd(), 17);
628 delete textinputObject;
631 void tst_qquicktextinput::isRightToLeft_data()
633 QTest::addColumn<QString>("text");
634 QTest::addColumn<bool>("emptyString");
635 QTest::addColumn<bool>("firstCharacter");
636 QTest::addColumn<bool>("lastCharacter");
637 QTest::addColumn<bool>("middleCharacter");
638 QTest::addColumn<bool>("startString");
639 QTest::addColumn<bool>("midString");
640 QTest::addColumn<bool>("endString");
642 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
643 QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
644 QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
645 QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
646 QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
647 QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
648 QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
651 void tst_qquicktextinput::isRightToLeft()
653 QFETCH(QString, text);
654 QFETCH(bool, emptyString);
655 QFETCH(bool, firstCharacter);
656 QFETCH(bool, lastCharacter);
657 QFETCH(bool, middleCharacter);
658 QFETCH(bool, startString);
659 QFETCH(bool, midString);
660 QFETCH(bool, endString);
662 QQuickTextInput textInput;
663 textInput.setText(text);
665 // first test that the right string is delivered to the QString::isRightToLeft()
666 QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
667 QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
668 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
669 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
670 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
671 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
673 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
674 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
676 // then test that the feature actually works
677 QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
678 QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
679 QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
680 QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
681 QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
682 QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
684 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
685 QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
688 void tst_qquicktextinput::moveCursorSelection_data()
690 QTest::addColumn<QString>("testStr");
691 QTest::addColumn<int>("cursorPosition");
692 QTest::addColumn<int>("movePosition");
693 QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
694 QTest::addColumn<int>("selectionStart");
695 QTest::addColumn<int>("selectionEnd");
696 QTest::addColumn<bool>("reversible");
698 // () contains the text selected by the cursor.
699 // <> contains the actual selection.
701 QTest::newRow("(t)he|characters")
702 << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
703 QTest::newRow("do(g)|characters")
704 << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
705 QTest::newRow("jum(p)ed|characters")
706 << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
707 QTest::newRow("jumped( )over|characters")
708 << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
709 QTest::newRow("(the )|characters")
710 << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
711 QTest::newRow("( dog)|characters")
712 << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
713 QTest::newRow("( jumped )|characters")
714 << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
715 QTest::newRow("th(e qu)ick|characters")
716 << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
717 QTest::newRow("la(zy d)og|characters")
718 << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
719 QTest::newRow("jum(ped ov)er|characters")
720 << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
721 QTest::newRow("()the|characters")
722 << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
723 QTest::newRow("dog()|characters")
724 << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
725 QTest::newRow("jum()ped|characters")
726 << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
728 QTest::newRow("<(t)he>|words")
729 << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
730 QTest::newRow("<do(g)>|words")
731 << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
732 QTest::newRow("<jum(p)ed>|words")
733 << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
734 QTest::newRow("<jumped( )>over|words,ltr")
735 << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
736 QTest::newRow("jumped<( )over>|words,rtl")
737 << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
738 QTest::newRow("<(the )>quick|words,ltr")
739 << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
740 QTest::newRow("<(the )quick>|words,rtl")
741 << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
742 QTest::newRow("<lazy( dog)>|words,ltr")
743 << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
744 QTest::newRow("lazy<( dog)>|words,rtl")
745 << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
746 QTest::newRow("<fox( jumped )>over|words,ltr")
747 << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
748 QTest::newRow("fox<( jumped )over>|words,rtl")
749 << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
750 QTest::newRow("<th(e qu)ick>|words")
751 << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
752 QTest::newRow("<la(zy d)og|words>")
753 << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
754 QTest::newRow("<jum(ped ov)er>|words")
755 << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
756 QTest::newRow("<()>the|words")
757 << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
758 QTest::newRow("dog<()>|words")
759 << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
760 QTest::newRow("jum<()>ped|words")
761 << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
763 QTest::newRow("Hello<(,)> |words")
764 << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
765 QTest::newRow("Hello<(, )>world|words,ltr")
766 << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
767 QTest::newRow("Hello<(, )world>|words,rtl")
768 << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
769 QTest::newRow("<Hel(lo, )>world|words,ltr")
770 << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
771 QTest::newRow("<Hel(lo, )world>|words,rtl")
772 << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
773 QTest::newRow("<Hel(lo)>,|words")
774 << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
775 QTest::newRow("Hello<()>,|words")
776 << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
777 QTest::newRow("Hello,<()>|words")
778 << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
779 QTest::newRow("Hello<,( )>world|words,ltr")
780 << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
781 QTest::newRow("Hello,<( )world>|words,rtl")
782 << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
783 QTest::newRow("Hello<,( world)>|words,ltr")
784 << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
785 QTest::newRow("Hello,<( world)>|words,rtl")
786 << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
787 QTest::newRow("Hello<,( world!)>|words,ltr")
788 << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
789 QTest::newRow("Hello,<( world!)>|words,rtl")
790 << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
791 QTest::newRow("Hello<(, world!)>|words")
792 << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
793 // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
795 // QTest::newRow("world<(!)>|words")
796 // << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
797 QTest::newRow("world!<()>)|words")
798 << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
799 QTest::newRow("world<()>!)|words")
800 << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
802 QTest::newRow("<(,)>olleH |words")
803 << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
804 QTest::newRow("<dlrow( ,)>olleH|words,ltr")
805 << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
806 QTest::newRow("dlrow<( ,)>olleH|words,rtl")
807 << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
808 QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
809 << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
810 QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
811 << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
812 QTest::newRow(",<(ol)leH>,|words")
813 << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
814 QTest::newRow(",<()>olleH|words")
815 << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
816 QTest::newRow("<()>,olleH|words")
817 << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
818 QTest::newRow("<dlrow( )>,olleH|words,ltr")
819 << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
820 QTest::newRow("dlrow<( ),>olleH|words,rtl")
821 << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
822 QTest::newRow("<(dlrow )>,olleH|words,ltr")
823 << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
824 QTest::newRow("<(dlrow ),>olleH|words,rtl")
825 << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
826 QTest::newRow("<(!dlrow )>,olleH|words,ltr")
827 << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
828 QTest::newRow("<(!dlrow ),>olleH|words,rtl")
829 << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
830 QTest::newRow("(!dlrow ,)olleH|words")
831 << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
832 QTest::newRow("<(!)>dlrow|words")
833 << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
834 QTest::newRow("<()>!dlrow|words")
835 << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
836 QTest::newRow("!<()>dlrow|words")
837 << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
839 QTest::newRow(" <s(pac)ey> text |words")
840 << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
841 QTest::newRow(" spacey <t(ex)t> |words")
842 << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
843 QTest::newRow("<( )>spacey text |words|ltr")
844 << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
845 QTest::newRow("<( )spacey> text |words|rtl")
846 << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
847 QTest::newRow("spacey <text( )>|words|ltr")
848 << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
850 // QTest::newRow("spacey text<( )>|words|rtl")
851 // << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
852 QTest::newRow("<()> spacey text |words")
853 << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
854 QTest::newRow(" spacey text <()>|words")
855 << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
858 void tst_qquicktextinput::moveCursorSelection()
860 QFETCH(QString, testStr);
861 QFETCH(int, cursorPosition);
862 QFETCH(int, movePosition);
863 QFETCH(QQuickTextInput::SelectionMode, mode);
864 QFETCH(int, selectionStart);
865 QFETCH(int, selectionEnd);
866 QFETCH(bool, reversible);
868 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
869 QDeclarativeComponent textinputComponent(&engine);
870 textinputComponent.setData(componentStr.toLatin1(), QUrl());
871 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
872 QVERIFY(textinputObject != 0);
874 textinputObject->setCursorPosition(cursorPosition);
875 textinputObject->moveCursorSelection(movePosition, mode);
877 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
878 QCOMPARE(textinputObject->selectionStart(), selectionStart);
879 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
882 textinputObject->setCursorPosition(movePosition);
883 textinputObject->moveCursorSelection(cursorPosition, mode);
885 QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
886 QCOMPARE(textinputObject->selectionStart(), selectionStart);
887 QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
890 delete textinputObject;
893 void tst_qquicktextinput::moveCursorSelectionSequence_data()
895 QTest::addColumn<QString>("testStr");
896 QTest::addColumn<int>("cursorPosition");
897 QTest::addColumn<int>("movePosition1");
898 QTest::addColumn<int>("movePosition2");
899 QTest::addColumn<int>("selection1Start");
900 QTest::addColumn<int>("selection1End");
901 QTest::addColumn<int>("selection2Start");
902 QTest::addColumn<int>("selection2End");
904 // () contains the text selected by the cursor.
905 // <> contains the actual selection.
906 // ^ is the revised cursor position.
907 // {} contains the revised selection.
909 QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
914 QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
919 QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
924 QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
929 QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
934 QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
939 QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
944 QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
949 QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
954 QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
959 QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
964 QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
969 QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
974 QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
979 QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
984 QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
989 QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
994 QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1000 QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1005 QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1011 QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1016 QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1022 QTest::newRow("{<(^} sp)acey> text |ltr")
1027 QTest::newRow("{<( ^}sp)acey> text |ltr")
1032 QTest::newRow("<( {s^p)acey>} text |rtl")
1037 QTest::newRow("<( {^sp)acey>} text |rtl")
1043 QTest::newRow(" spacey <te(xt {^)>}|rtl")
1049 // QTest::newRow(" spacey <te(xt{^ )>}|rtl")
1051 // << 15 << 12 << 14
1054 QTest::newRow(" spacey {<te(x^t} )>|ltr")
1060 // QTest::newRow(" spacey {<te(xt^} )>|ltr")
1062 // << 12 << 15 << 14
1067 void tst_qquicktextinput::moveCursorSelectionSequence()
1069 QFETCH(QString, testStr);
1070 QFETCH(int, cursorPosition);
1071 QFETCH(int, movePosition1);
1072 QFETCH(int, movePosition2);
1073 QFETCH(int, selection1Start);
1074 QFETCH(int, selection1End);
1075 QFETCH(int, selection2Start);
1076 QFETCH(int, selection2End);
1078 QString componentStr = "import QtQuick 2.0\nTextInput { text: \""+ testStr +"\"; }";
1079 QDeclarativeComponent textinputComponent(&engine);
1080 textinputComponent.setData(componentStr.toLatin1(), QUrl());
1081 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1082 QVERIFY(textinputObject != 0);
1084 textinputObject->setCursorPosition(cursorPosition);
1086 textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1087 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1088 QCOMPARE(textinputObject->selectionStart(), selection1Start);
1089 QCOMPARE(textinputObject->selectionEnd(), selection1End);
1091 textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1092 QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1093 QCOMPARE(textinputObject->selectionStart(), selection2Start);
1094 QCOMPARE(textinputObject->selectionEnd(), selection2End);
1096 delete textinputObject;
1099 void tst_qquicktextinput::dragMouseSelection()
1101 QString qmlfile = testFile("mouseselection_true.qml");
1103 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1106 canvas.requestActivateWindow();
1107 QTest::qWaitForWindowShown(&canvas);
1109 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1111 QVERIFY(canvas.rootObject() != 0);
1112 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1113 QVERIFY(textInputObject != 0);
1115 // press-and-drag-and-release from x1 to x2
1118 int y = textInputObject->height()/2;
1119 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1120 QTest::mouseMove(&canvas, QPoint(x2, y));
1121 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1124 QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1125 QVERIFY(str1.length() > 3);
1127 // press and drag the current selection.
1130 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1131 QTest::mouseMove(&canvas, QPoint(x2, y));
1132 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1134 QString str2 = textInputObject->selectedText();
1135 QVERIFY(str2.length() > 3);
1137 QVERIFY(str1 != str2);
1140 void tst_qquicktextinput::mouseSelectionMode_data()
1142 QTest::addColumn<QString>("qmlfile");
1143 QTest::addColumn<bool>("selectWords");
1146 QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1147 QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1148 QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1151 void tst_qquicktextinput::mouseSelectionMode()
1153 QFETCH(QString, qmlfile);
1154 QFETCH(bool, selectWords);
1156 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1158 QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1161 canvas.requestActivateWindow();
1162 QTest::qWaitForWindowShown(&canvas);
1163 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1165 QVERIFY(canvas.rootObject() != 0);
1166 QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1167 QVERIFY(textInputObject != 0);
1169 // press-and-drag-and-release from x1 to x2
1172 int y = textInputObject->height()/2;
1173 QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1174 QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1175 QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1178 QTRY_COMPARE(textInputObject->selectedText(), text);
1180 QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1181 QVERIFY(textInputObject->selectedText() != text);
1185 void tst_qquicktextinput::horizontalAlignment_data()
1187 QTest::addColumn<int>("hAlign");
1188 QTest::addColumn<QString>("expectfile");
1190 QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1191 QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1192 QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1195 void tst_qquicktextinput::horizontalAlignment()
1197 QSKIP("Image comparison of text is almost guaranteed to fail during development");
1199 QFETCH(int, hAlign);
1200 QFETCH(QString, expectfile);
1202 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1205 canvas.requestActivateWindow();
1206 QTest::qWaitForWindowShown(&canvas);
1207 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1208 QObject *ob = canvas.rootObject();
1210 ob->setProperty("horizontalAlignment",hAlign);
1211 QImage actual = canvas.grabFrameBuffer();
1213 expectfile = createExpectedFileIfNotFound(expectfile, actual);
1215 QImage expect(expectfile);
1217 QCOMPARE(actual,expect);
1220 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1222 PlatformInputContext platformInputContext;
1223 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
1224 inputPanelPrivate->testContext = &platformInputContext;
1226 QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1227 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1228 QVERIFY(textInput != 0);
1231 const QString rtlText = textInput->text();
1233 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1234 QVERIFY(textInputPrivate != 0);
1235 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1236 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1238 // implicit alignment should follow the reading direction of RTL text
1239 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1240 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1241 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1242 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1244 // explicitly left aligned
1245 textInput->setHAlign(QQuickTextInput::AlignLeft);
1246 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1247 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1248 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1250 // explicitly right aligned
1251 textInput->setHAlign(QQuickTextInput::AlignRight);
1252 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1253 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1254 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1255 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1257 // explicitly center aligned
1258 textInput->setHAlign(QQuickTextInput::AlignHCenter);
1259 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1260 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1261 QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0);
1262 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width());
1264 // reseted alignment should go back to following the text reading direction
1265 textInput->resetHAlign();
1266 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1267 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1268 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1269 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1271 // mirror the text item
1272 QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1274 // mirrored implicit alignment should continue to follow the reading direction of the text
1275 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1276 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1277 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1278 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1280 // explicitly right aligned behaves as left aligned
1281 textInput->setHAlign(QQuickTextInput::AlignRight);
1282 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1283 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1284 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1286 // mirrored explicitly left aligned behaves as right aligned
1287 textInput->setHAlign(QQuickTextInput::AlignLeft);
1288 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1289 QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1290 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1291 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1293 // disable mirroring
1294 QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1295 QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1296 textInput->resetHAlign();
1298 // English text should be implicitly left aligned
1299 textInput->setText("Hello world!");
1300 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1301 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1303 canvas.requestActivateWindow();
1304 QTest::qWaitForWindowShown(&canvas);
1305 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1307 // If there is no commited text, the preedit text should determine the alignment.
1308 textInput->setText(QString());
1309 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1310 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1311 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1312 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1314 // Clear pre-edit text. TextInput should maybe do this itself on setText, but that may be
1315 // redundant as an actual input method may take care of it.
1316 { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1318 // empty text with implicit alignment follows the system locale-based
1319 // keyboard input direction from QInputPanel::inputDirection()
1320 textInput->setText("");
1321 platformInputContext.setInputDirection(Qt::LeftToRight);
1322 QVERIFY(qApp->inputPanel()->inputDirection() == Qt::LeftToRight);
1323 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1324 QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1326 QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1327 platformInputContext.setInputDirection(Qt::RightToLeft);
1328 QVERIFY(qApp->inputPanel()->inputDirection() == Qt::RightToLeft);
1329 QCOMPARE(cursorRectangleSpy.count(), 1);
1330 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1331 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1332 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1334 // set input direction while having content
1335 platformInputContext.setInputDirection(Qt::LeftToRight);
1336 textInput->setText("a");
1337 platformInputContext.setInputDirection(Qt::RightToLeft);
1338 QTest::keyClick(&canvas, Qt::Key_Backspace);
1339 QVERIFY(textInput->text().isEmpty());
1340 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1341 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1342 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1344 // input direction changed while not having focus
1345 platformInputContext.setInputDirection(Qt::LeftToRight);
1346 textInput->setFocus(false);
1347 platformInputContext.setInputDirection(Qt::RightToLeft);
1348 textInput->setFocus(true);
1349 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1350 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1351 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1353 textInput->setHAlign(QQuickTextInput::AlignRight);
1354 QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1355 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1356 QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1359 void tst_qquicktextinput::verticalAlignment()
1361 QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1362 QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1363 QVERIFY(textInput != 0);
1366 QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1367 QVERIFY(textInputPrivate != 0);
1369 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1370 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2);
1373 textInput->setVAlign(QQuickTextInput::AlignBottom);
1374 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1375 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2);
1377 // explicitly center aligned
1378 textInput->setVAlign(QQuickTextInput::AlignVCenter);
1379 QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1380 QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2);
1381 QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2);
1384 void tst_qquicktextinput::boundingRect()
1386 QDeclarativeComponent component(&engine);
1387 component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1388 QScopedPointer<QObject> object(component.create());
1389 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1392 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1393 QCOMPARE(input->height(), input->boundingRect().height());
1395 input->setText("Hello World");
1396 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1397 QCOMPARE(input->height(), input->boundingRect().height());
1399 // bounding rect shouldn't exceed the size of the item, expect for the cursor width;
1400 input->setWidth(input->width() / 2);
1401 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1402 QCOMPARE(input->height(), input->boundingRect().height());
1404 input->setHeight(input->height() * 2);
1405 QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1406 QCOMPARE(input->height(), input->boundingRect().height());
1408 QDeclarativeComponent cursorComponent(&engine);
1409 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1411 input->setCursorDelegate(&cursorComponent);
1413 // If a cursor delegate is used it's size should determine the excess width.
1414 QCOMPARE(input->width() + 8, input->boundingRect().width());
1415 QCOMPARE(input->height(), input->boundingRect().height());
1418 void tst_qquicktextinput::positionAt()
1420 QQuickView canvas(testFileUrl("positionAt.qml"));
1421 QVERIFY(canvas.rootObject() != 0);
1423 canvas.requestActivateWindow();
1424 QTest::qWaitForWindowShown(&canvas);
1426 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1427 QVERIFY(textinputObject != 0);
1429 // Check autoscrolled...
1431 int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1433 QTextLayout layout(textinputObject->text());
1434 layout.setFont(textinputObject->font());
1436 if (!qmlDisableDistanceField()) {
1438 option.setUseDesignMetrics(true);
1439 layout.setTextOption(option);
1441 layout.beginLayout();
1442 QTextLine line = layout.createLine();
1445 int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1446 int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1447 int textWidth = floor(line.horizontalAdvance());
1449 QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1450 QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1452 int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1453 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1454 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1456 // Check without autoscroll...
1457 textinputObject->setAutoScroll(false);
1458 pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1460 textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1461 textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1463 QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1464 QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1466 x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1467 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1468 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1470 const qreal x0 = textinputObject->positionToRectangle(pos).x();
1471 const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1473 QString preeditText = textinputObject->text().mid(0, pos);
1474 textinputObject->setText(textinputObject->text().mid(pos));
1475 textinputObject->setCursorPosition(0);
1477 { QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1478 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1480 // Check all points within the preedit text return the same position.
1481 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1482 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1483 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1485 // Verify positioning returns to normal after the preedit text.
1486 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1487 QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1489 { QInputMethodEvent inputEvent;
1490 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1493 textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1495 const qreal y0 = line.height() / 2;
1496 const qreal y1 = line.height() * 3 / 2;
1498 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1499 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1501 int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1502 QVERIFY(newLinePos > pos);
1503 QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1506 void tst_qquicktextinput::maxLength()
1508 QQuickView canvas(testFileUrl("maxLength.qml"));
1509 QVERIFY(canvas.rootObject() != 0);
1511 canvas.requestActivateWindow();
1512 QTest::qWaitForWindowShown(&canvas);
1514 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1515 QVERIFY(textinputObject != 0);
1516 QVERIFY(textinputObject->text().isEmpty());
1517 QVERIFY(textinputObject->maxLength() == 10);
1518 foreach (const QString &str, standard) {
1519 QVERIFY(textinputObject->text().length() <= 10);
1520 textinputObject->setText(str);
1521 QVERIFY(textinputObject->text().length() <= 10);
1524 textinputObject->setText("");
1525 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1526 for (int i=0; i<20; i++) {
1527 QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1528 //simulateKey(&canvas, Qt::Key_A);
1529 QTest::keyPress(&canvas, Qt::Key_A);
1530 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1535 void tst_qquicktextinput::masks()
1537 //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1538 //QString componentStr = "import QtQuick 2.0\nTextInput { inputMask: 'HHHHhhhh'; }";
1539 QQuickView canvas(testFileUrl("masks.qml"));
1541 canvas.requestActivateWindow();
1542 QVERIFY(canvas.rootObject() != 0);
1543 QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1544 QVERIFY(textinputObject != 0);
1545 QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1546 QVERIFY(textinputObject->text().length() == 0);
1547 QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1548 QCOMPARE(textinputObject->length(), 8);
1549 for (int i=0; i<10; i++) {
1550 QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1551 QCOMPARE(textinputObject->length(), 8);
1552 QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1553 QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1554 QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1555 //simulateKey(&canvas, Qt::Key_A);
1556 QTest::keyPress(&canvas, Qt::Key_A);
1557 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1562 void tst_qquicktextinput::validators()
1564 // Note that this test assumes that the validators are working properly
1565 // so you may need to run their tests first. All validators are checked
1566 // here to ensure that their exposure to QML is working.
1568 QLocale::setDefault(QLocale(QStringLiteral("C")));
1570 QQuickView canvas(testFileUrl("validators.qml"));
1572 canvas.requestActivateWindow();
1574 QVERIFY(canvas.rootObject() != 0);
1576 QLocale defaultLocale;
1577 QLocale enLocale("en");
1578 QLocale deLocale("de_DE");
1580 QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1582 QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1584 QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1585 QVERIFY(intValidator);
1586 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1587 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1588 intValidator->setLocaleName(enLocale.name());
1589 QCOMPARE(intValidator->localeName(), enLocale.name());
1590 QCOMPARE(intInput->validator()->locale(), enLocale);
1591 intValidator->resetLocaleName();
1592 QCOMPARE(intValidator->localeName(), defaultLocale.name());
1593 QCOMPARE(intInput->validator()->locale(), defaultLocale);
1595 intInput->setFocus(true);
1596 QTRY_VERIFY(intInput->hasActiveFocus());
1597 QCOMPARE(intInput->hasAcceptableInput(), false);
1598 QCOMPARE(intInput->property("acceptable").toBool(), false);
1599 QTest::keyPress(&canvas, Qt::Key_1);
1600 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1602 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1603 QCOMPARE(intInput->hasAcceptableInput(), false);
1604 QCOMPARE(intInput->property("acceptable").toBool(), false);
1605 QCOMPARE(intSpy.count(), 0);
1606 QTest::keyPress(&canvas, Qt::Key_2);
1607 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1609 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1610 QCOMPARE(intInput->hasAcceptableInput(), false);
1611 QCOMPARE(intInput->property("acceptable").toBool(), false);
1612 QCOMPARE(intSpy.count(), 0);
1613 QTest::keyPress(&canvas, Qt::Key_Period);
1614 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1616 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1617 QCOMPARE(intInput->hasAcceptableInput(), false);
1618 QTest::keyPress(&canvas, Qt::Key_Comma);
1619 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1621 QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1622 QCOMPARE(intInput->hasAcceptableInput(), false);
1623 QTest::keyPress(&canvas, Qt::Key_Backspace);
1624 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1626 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1627 QCOMPARE(intInput->hasAcceptableInput(), false);
1628 intValidator->setLocaleName(deLocale.name());
1629 QTest::keyPress(&canvas, Qt::Key_Period);
1630 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1632 QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1633 QCOMPARE(intInput->hasAcceptableInput(), false);
1634 QTest::keyPress(&canvas, Qt::Key_Backspace);
1635 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1637 QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1638 QCOMPARE(intInput->hasAcceptableInput(), false);
1639 intValidator->resetLocaleName();
1640 QTest::keyPress(&canvas, Qt::Key_1);
1641 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1643 QCOMPARE(intInput->text(), QLatin1String("11"));
1644 QCOMPARE(intInput->hasAcceptableInput(), true);
1645 QCOMPARE(intInput->property("acceptable").toBool(), true);
1646 QCOMPARE(intSpy.count(), 1);
1647 QTest::keyPress(&canvas, Qt::Key_0);
1648 QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1650 QCOMPARE(intInput->text(), QLatin1String("11"));
1651 QCOMPARE(intInput->hasAcceptableInput(), true);
1652 QCOMPARE(intInput->property("acceptable").toBool(), true);
1653 QCOMPARE(intSpy.count(), 1);
1655 QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1657 QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1659 QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1660 QVERIFY(dblValidator);
1661 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1662 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1663 dblValidator->setLocaleName(enLocale.name());
1664 QCOMPARE(dblValidator->localeName(), enLocale.name());
1665 QCOMPARE(dblInput->validator()->locale(), enLocale);
1666 dblValidator->resetLocaleName();
1667 QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1668 QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1670 dblInput->setFocus(true);
1671 QVERIFY(dblInput->hasActiveFocus() == true);
1672 QCOMPARE(dblInput->hasAcceptableInput(), false);
1673 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1674 QTest::keyPress(&canvas, Qt::Key_1);
1675 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1677 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1678 QCOMPARE(dblInput->hasAcceptableInput(), false);
1679 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1680 QCOMPARE(dblSpy.count(), 0);
1681 QTest::keyPress(&canvas, Qt::Key_2);
1682 QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1684 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1685 QCOMPARE(dblInput->hasAcceptableInput(), true);
1686 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1687 QCOMPARE(dblSpy.count(), 1);
1688 QTest::keyPress(&canvas, Qt::Key_Comma);
1689 QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1691 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1692 QCOMPARE(dblInput->hasAcceptableInput(), true);
1693 QTest::keyPress(&canvas, Qt::Key_1);
1694 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1696 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1697 QCOMPARE(dblInput->hasAcceptableInput(), true);
1698 dblValidator->setLocaleName(deLocale.name());
1699 QCOMPARE(dblInput->hasAcceptableInput(), true);
1700 QTest::keyPress(&canvas, Qt::Key_1);
1701 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1703 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1704 QCOMPARE(dblInput->hasAcceptableInput(), true);
1705 QTest::keyPress(&canvas, Qt::Key_1);
1706 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1708 QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1709 QCOMPARE(dblInput->hasAcceptableInput(), true);
1710 QTest::keyPress(&canvas, Qt::Key_Backspace);
1711 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1713 QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1714 QCOMPARE(dblInput->hasAcceptableInput(), true);
1715 QTest::keyPress(&canvas, Qt::Key_Backspace);
1716 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1718 QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1719 QCOMPARE(dblInput->hasAcceptableInput(), true);
1720 QTest::keyPress(&canvas, Qt::Key_Backspace);
1721 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1723 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1724 QCOMPARE(dblInput->hasAcceptableInput(), true);
1725 dblValidator->resetLocaleName();
1726 QTest::keyPress(&canvas, Qt::Key_Period);
1727 QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1729 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1730 QCOMPARE(dblInput->hasAcceptableInput(), true);
1731 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1732 QCOMPARE(dblSpy.count(), 1);
1733 QTest::keyPress(&canvas, Qt::Key_1);
1734 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1736 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1737 QCOMPARE(dblInput->hasAcceptableInput(), true);
1738 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1739 QCOMPARE(dblSpy.count(), 1);
1740 QTest::keyPress(&canvas, Qt::Key_1);
1741 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1743 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1744 QCOMPARE(dblInput->hasAcceptableInput(), true);
1745 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1746 QCOMPARE(dblSpy.count(), 1);
1747 QTest::keyPress(&canvas, Qt::Key_1);
1748 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1750 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1751 QCOMPARE(dblInput->hasAcceptableInput(), true);
1752 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1753 QCOMPARE(dblSpy.count(), 1);
1755 // Ensure the validator doesn't prevent characters being removed.
1756 dblInput->setValidator(intInput->validator());
1757 QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1758 QCOMPARE(dblInput->hasAcceptableInput(), false);
1759 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1760 QCOMPARE(dblSpy.count(), 2);
1761 QTest::keyPress(&canvas, Qt::Key_Backspace);
1762 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1764 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1765 QCOMPARE(dblInput->hasAcceptableInput(), false);
1766 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1767 QCOMPARE(dblSpy.count(), 2);
1768 // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1769 QTest::keyPress(&canvas, Qt::Key_1);
1770 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1772 QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1773 QCOMPARE(dblInput->hasAcceptableInput(), false);
1774 QCOMPARE(dblSpy.count(), 2);
1775 QTest::keyPress(&canvas, Qt::Key_Backspace);
1776 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1778 QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1779 QCOMPARE(dblInput->hasAcceptableInput(), false);
1780 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1781 QCOMPARE(dblSpy.count(), 2);
1782 QTest::keyPress(&canvas, Qt::Key_Backspace);
1783 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1785 QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1786 QCOMPARE(dblInput->hasAcceptableInput(), false);
1787 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1788 QCOMPARE(dblSpy.count(), 2);
1789 QTest::keyPress(&canvas, Qt::Key_Backspace);
1790 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1792 QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1793 QCOMPARE(dblInput->hasAcceptableInput(), false);
1794 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1795 QCOMPARE(dblSpy.count(), 2);
1796 QTest::keyPress(&canvas, Qt::Key_Backspace);
1797 QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1799 QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1800 QCOMPARE(dblInput->hasAcceptableInput(), false);
1801 QCOMPARE(dblInput->property("acceptable").toBool(), false);
1802 QCOMPARE(dblSpy.count(), 2);
1803 QTest::keyPress(&canvas, Qt::Key_1);
1804 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1806 QCOMPARE(dblInput->text(), QLatin1String("11"));
1807 QCOMPARE(dblInput->property("acceptable").toBool(), true);
1808 QCOMPARE(dblInput->hasAcceptableInput(), true);
1809 QCOMPARE(dblSpy.count(), 3);
1811 QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1813 QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
1814 strInput->setFocus(true);
1815 QVERIFY(strInput->hasActiveFocus() == true);
1816 QCOMPARE(strInput->hasAcceptableInput(), false);
1817 QCOMPARE(strInput->property("acceptable").toBool(), false);
1818 QTest::keyPress(&canvas, Qt::Key_1);
1819 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1821 QTRY_COMPARE(strInput->text(), QLatin1String(""));
1822 QCOMPARE(strInput->hasAcceptableInput(), false);
1823 QCOMPARE(strInput->property("acceptable").toBool(), false);
1824 QCOMPARE(strSpy.count(), 0);
1825 QTest::keyPress(&canvas, Qt::Key_A);
1826 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1828 QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1829 QCOMPARE(strInput->hasAcceptableInput(), false);
1830 QCOMPARE(strInput->property("acceptable").toBool(), false);
1831 QCOMPARE(strSpy.count(), 0);
1832 QTest::keyPress(&canvas, Qt::Key_A);
1833 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1835 QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1836 QCOMPARE(strInput->hasAcceptableInput(), true);
1837 QCOMPARE(strInput->property("acceptable").toBool(), true);
1838 QCOMPARE(strSpy.count(), 1);
1839 QTest::keyPress(&canvas, Qt::Key_A);
1840 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1842 QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1843 QCOMPARE(strInput->hasAcceptableInput(), true);
1844 QCOMPARE(strInput->property("acceptable").toBool(), true);
1845 QCOMPARE(strSpy.count(), 1);
1846 QTest::keyPress(&canvas, Qt::Key_A);
1847 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1849 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1850 QCOMPARE(strInput->hasAcceptableInput(), true);
1851 QCOMPARE(strInput->property("acceptable").toBool(), true);
1852 QCOMPARE(strSpy.count(), 1);
1853 QTest::keyPress(&canvas, Qt::Key_A);
1854 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1856 QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1857 QCOMPARE(strInput->hasAcceptableInput(), true);
1858 QCOMPARE(strInput->property("acceptable").toBool(), true);
1859 QCOMPARE(strSpy.count(), 1);
1861 QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
1862 QVERIFY(unvalidatedInput);
1863 QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
1864 unvalidatedInput->setFocus(true);
1865 QVERIFY(unvalidatedInput->hasActiveFocus() == true);
1866 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1867 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1868 QTest::keyPress(&canvas, Qt::Key_1);
1869 QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1871 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
1872 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1873 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1874 QCOMPARE(unvalidatedSpy.count(), 0);
1875 QTest::keyPress(&canvas, Qt::Key_A);
1876 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1878 QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
1879 QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1880 QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1881 QCOMPARE(unvalidatedSpy.count(), 0);
1884 void tst_qquicktextinput::inputMethods()
1886 QQuickView canvas(testFileUrl("inputmethods.qml"));
1888 canvas.requestActivateWindow();
1889 QTest::qWaitForWindowShown(&canvas);
1891 // test input method hints
1892 QVERIFY(canvas.rootObject() != 0);
1893 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1894 QVERIFY(input != 0);
1895 QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1896 input->setInputMethodHints(Qt::ImhUppercaseOnly);
1897 QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1899 input->setFocus(true);
1900 QVERIFY(input->hasActiveFocus() == true);
1901 // test that input method event is committed
1902 QInputMethodEvent event;
1903 event.setCommitString( "My ", -12, 0);
1904 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1905 QCOMPARE(input->text(), QString("My Hello world!"));
1907 input->setCursorPosition(2);
1908 event.setCommitString("Your", -2, 2);
1909 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1910 QCOMPARE(input->text(), QString("Your Hello world!"));
1911 QCOMPARE(input->cursorPosition(), 4);
1913 input->setCursorPosition(7);
1914 event.setCommitString("Goodbye", -2, 5);
1915 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1916 QCOMPARE(input->text(), QString("Your Goodbye world!"));
1917 QCOMPARE(input->cursorPosition(), 12);
1919 input->setCursorPosition(8);
1920 event.setCommitString("Our", -8, 4);
1921 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1922 QCOMPARE(input->text(), QString("Our Goodbye world!"));
1923 QCOMPARE(input->cursorPosition(), 7);
1925 // test that basic tentative commit gets to text property on preedit state
1927 QList<QInputMethodEvent::Attribute> attributes;
1928 QInputMethodEvent preeditEvent("test", attributes);
1929 preeditEvent.setTentativeCommitString("test");
1930 QGuiApplication::sendEvent(input, &preeditEvent);
1931 QCOMPARE(input->text(), QString("test"));
1933 // tentative commit not allowed present in surrounding text
1934 QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
1935 QGuiApplication::sendEvent(input, &queryEvent);
1936 QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
1938 // if text with tentative commit does not validate, not allowed to be part of text property
1939 input->setText(""); // ensure input state is reset
1940 QValidator *validator = new QIntValidator(0, 100);
1941 input->setValidator(validator);
1942 QGuiApplication::sendEvent(input, &preeditEvent);
1943 QCOMPARE(input->text(), QString(""));
1944 input->setValidator(0);
1947 // input should reset selection even if replacement parameters are out of bounds
1948 input->setText("text");
1949 input->setCursorPosition(0);
1950 input->moveCursorSelection(input->text().length());
1951 event.setCommitString("replacement", -input->text().length(), input->text().length());
1952 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1953 QCOMPARE(input->selectionStart(), input->selectionEnd());
1957 TextInput element should only handle left/right keys until the cursor reaches
1958 the extent of the text, then they should ignore the keys.
1961 void tst_qquicktextinput::navigation()
1963 QQuickView canvas(testFileUrl("navigation.qml"));
1965 canvas.requestActivateWindow();
1967 QVERIFY(canvas.rootObject() != 0);
1969 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1971 QVERIFY(input != 0);
1972 input->setCursorPosition(0);
1973 QTRY_VERIFY(input->hasActiveFocus() == true);
1974 simulateKey(&canvas, Qt::Key_Left);
1975 QVERIFY(input->hasActiveFocus() == false);
1976 simulateKey(&canvas, Qt::Key_Right);
1977 QVERIFY(input->hasActiveFocus() == true);
1978 //QT-2944: If text is selected, ensure we deselect upon cursor motion
1979 input->setCursorPosition(input->text().length());
1980 input->select(0,input->text().length());
1981 QVERIFY(input->selectionStart() != input->selectionEnd());
1982 simulateKey(&canvas, Qt::Key_Right);
1983 QVERIFY(input->selectionStart() == input->selectionEnd());
1984 QVERIFY(input->selectionStart() == input->text().length());
1985 QVERIFY(input->hasActiveFocus() == true);
1986 simulateKey(&canvas, Qt::Key_Right);
1987 QVERIFY(input->hasActiveFocus() == false);
1988 simulateKey(&canvas, Qt::Key_Left);
1989 QVERIFY(input->hasActiveFocus() == true);
1991 // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1992 input->setCursorPosition(2);
1993 QCOMPARE(input->cursorPosition(),2);
1994 simulateKey(&canvas, Qt::Key_Up);
1995 QCOMPARE(input->cursorPosition(),2);
1996 simulateKey(&canvas, Qt::Key_Down);
1997 QCOMPARE(input->cursorPosition(),2);
2000 void tst_qquicktextinput::navigation_RTL()
2002 QQuickView canvas(testFileUrl("navigation.qml"));
2004 canvas.requestActivateWindow();
2006 QVERIFY(canvas.rootObject() != 0);
2008 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2010 QVERIFY(input != 0);
2011 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2012 input->setText(QString::fromUtf16(arabic_str, 11));
2014 input->setCursorPosition(0);
2015 QTRY_VERIFY(input->hasActiveFocus() == true);
2018 simulateKey(&canvas, Qt::Key_Right);
2019 QVERIFY(input->hasActiveFocus() == false);
2022 simulateKey(&canvas, Qt::Key_Left);
2023 QVERIFY(input->hasActiveFocus() == true);
2025 input->setCursorPosition(input->text().length());
2026 QVERIFY(input->hasActiveFocus() == true);
2029 simulateKey(&canvas, Qt::Key_Left);
2030 QVERIFY(input->hasActiveFocus() == false);
2033 simulateKey(&canvas, Qt::Key_Right);
2034 QVERIFY(input->hasActiveFocus() == true);
2037 void tst_qquicktextinput::copyAndPaste() {
2038 #ifndef QT_NO_CLIPBOARD
2042 PasteboardRef pasteboard;
2043 OSStatus status = PasteboardCreate(0, &pasteboard);
2044 if (status == noErr)
2045 CFRelease(pasteboard);
2047 QSKIP("This machine doesn't support the clipboard");
2051 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2052 QDeclarativeComponent textInputComponent(&engine);
2053 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2054 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2055 QVERIFY(textInput != 0);
2058 QCOMPARE(textInput->text().length(), 12);
2059 textInput->select(0, textInput->text().length());;
2061 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2062 QCOMPARE(textInput->selectedText().length(), 12);
2063 textInput->setCursorPosition(0);
2064 QVERIFY(textInput->canPaste());
2066 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2067 QCOMPARE(textInput->text().length(), 24);
2070 QVERIFY(textInput->canPaste());
2071 textInput->setReadOnly(true);
2072 QVERIFY(!textInput->canPaste());
2073 textInput->setReadOnly(false);
2074 QVERIFY(textInput->canPaste());
2077 textInput->setCursorPosition(0);
2078 textInput->selectWord();
2079 QCOMPARE(textInput->selectedText(), QString("Hello"));
2081 // select all and cut
2082 textInput->selectAll();
2084 QCOMPARE(textInput->text().length(), 0);
2086 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2087 QCOMPARE(textInput->text().length(), 24);
2089 // clear copy buffer
2090 QClipboard *clipboard = QGuiApplication::clipboard();
2093 QVERIFY(!textInput->canPaste());
2095 // test that copy functionality is disabled
2096 // when echo mode is set to hide text/password mode
2099 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2100 textInput->setEchoMode(echoMode);
2101 textInput->setText("My password");
2102 textInput->select(0, textInput->text().length());;
2104 if (echoMode == QQuickTextInput::Normal) {
2105 QVERIFY(!clipboard->text().isEmpty());
2106 QCOMPARE(clipboard->text(), QString("My password"));
2109 QVERIFY(clipboard->text().isEmpty());
2118 void tst_qquicktextinput::copyAndPasteKeySequence() {
2119 #ifndef QT_NO_CLIPBOARD
2123 PasteboardRef pasteboard;
2124 OSStatus status = PasteboardCreate(0, &pasteboard);
2125 if (status == noErr)
2126 CFRelease(pasteboard);
2128 QSKIP("This machine doesn't support the clipboard");
2132 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2133 QDeclarativeComponent textInputComponent(&engine);
2134 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2135 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2136 QVERIFY(textInput != 0);
2138 QQuickCanvas canvas;
2139 textInput->setParentItem(canvas.rootItem());
2141 canvas.requestActivateWindow();
2142 QTest::qWaitForWindowShown(&canvas);
2143 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2146 QVERIFY(textInput->hasActiveFocus());
2147 QCOMPARE(textInput->text().length(), 12);
2148 textInput->select(0, textInput->text().length());
2149 simulateKeys(&canvas, QKeySequence::Copy);
2150 QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2151 QCOMPARE(textInput->selectedText().length(), 12);
2152 textInput->setCursorPosition(0);
2153 QVERIFY(textInput->canPaste());
2154 simulateKeys(&canvas, QKeySequence::Paste);
2155 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2156 QCOMPARE(textInput->text().length(), 24);
2158 // select all and cut
2159 simulateKeys(&canvas, QKeySequence::SelectAll);
2160 simulateKeys(&canvas, QKeySequence::Cut);
2161 QCOMPARE(textInput->text().length(), 0);
2162 simulateKeys(&canvas, QKeySequence::Paste);
2163 QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2164 QCOMPARE(textInput->text().length(), 24);
2166 // clear copy buffer
2167 QClipboard *clipboard = QGuiApplication::clipboard();
2170 QVERIFY(!textInput->canPaste());
2172 // test that copy functionality is disabled
2173 // when echo mode is set to hide text/password mode
2176 QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2177 textInput->setEchoMode(echoMode);
2178 textInput->setText("My password");
2179 textInput->select(0, textInput->text().length());;
2180 simulateKeys(&canvas, QKeySequence::Copy);
2181 if (echoMode == QQuickTextInput::Normal) {
2182 QVERIFY(!clipboard->text().isEmpty());
2183 QCOMPARE(clipboard->text(), QString("My password"));
2186 QVERIFY(clipboard->text().isEmpty());
2195 void tst_qquicktextinput::canPasteEmpty() {
2196 #ifndef QT_NO_CLIPBOARD
2198 QGuiApplication::clipboard()->clear();
2200 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2201 QDeclarativeComponent textInputComponent(&engine);
2202 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2203 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2204 QVERIFY(textInput != 0);
2206 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2207 QCOMPARE(textInput->canPaste(), cp);
2212 void tst_qquicktextinput::canPaste() {
2213 #ifndef QT_NO_CLIPBOARD
2215 QGuiApplication::clipboard()->setText("Some text");
2217 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2218 QDeclarativeComponent textInputComponent(&engine);
2219 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2220 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2221 QVERIFY(textInput != 0);
2223 bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2224 QCOMPARE(textInput->canPaste(), cp);
2229 void tst_qquicktextinput::passwordCharacter()
2231 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2232 QDeclarativeComponent textInputComponent(&engine);
2233 textInputComponent.setData(componentStr.toLatin1(), QUrl());
2234 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2235 QVERIFY(textInput != 0);
2237 textInput->setPasswordCharacter("X");
2238 qreal implicitWidth = textInput->implicitWidth();
2239 textInput->setPasswordCharacter(".");
2241 // QTBUG-12383 content is updated and redrawn
2242 QVERIFY(textInput->implicitWidth() < implicitWidth);
2247 void tst_qquicktextinput::cursorDelegate()
2249 QQuickView view(testFileUrl("cursorTest.qml"));
2251 view.requestActivateWindow();
2252 QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2253 QVERIFY(textInputObject != 0);
2254 QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2255 //Test Delegate gets created
2256 textInputObject->setFocus(true);
2257 QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2258 QVERIFY(delegateObject);
2259 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2260 //Test Delegate gets moved
2261 for (int i=0; i<= textInputObject->text().length(); i++) {
2262 textInputObject->setCursorPosition(i);
2263 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2264 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2266 textInputObject->setCursorPosition(0);
2267 QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2268 QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2269 //Test Delegate gets deleted
2270 textInputObject->setCursorDelegate(0);
2271 QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2274 void tst_qquicktextinput::cursorVisible()
2276 QQuickView view(testFileUrl("cursorVisible.qml"));
2278 view.requestActivateWindow();
2279 QTest::qWaitForWindowShown(&view);
2280 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2282 QQuickTextInput input;
2283 input.componentComplete();
2284 QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2286 QCOMPARE(input.isCursorVisible(), false);
2288 input.setCursorVisible(true);
2289 QCOMPARE(input.isCursorVisible(), true);
2290 QCOMPARE(spy.count(), 1);
2292 input.setCursorVisible(false);
2293 QCOMPARE(input.isCursorVisible(), false);
2294 QCOMPARE(spy.count(), 2);
2296 input.setFocus(true);
2297 QCOMPARE(input.isCursorVisible(), false);
2298 QCOMPARE(spy.count(), 2);
2300 input.setParentItem(view.rootObject());
2301 QCOMPARE(input.isCursorVisible(), true);
2302 QCOMPARE(spy.count(), 3);
2304 input.setFocus(false);
2305 QCOMPARE(input.isCursorVisible(), false);
2306 QCOMPARE(spy.count(), 4);
2308 input.setFocus(true);
2309 QCOMPARE(input.isCursorVisible(), true);
2310 QCOMPARE(spy.count(), 5);
2312 QQuickView alternateView;
2313 alternateView.show();
2314 alternateView.requestActivateWindow();
2315 QTest::qWaitForWindowShown(&alternateView);
2317 QCOMPARE(input.isCursorVisible(), false);
2318 QCOMPARE(spy.count(), 6);
2320 view.requestActivateWindow();
2321 QTest::qWaitForWindowShown(&view);
2322 QCOMPARE(input.isCursorVisible(), true);
2323 QCOMPARE(spy.count(), 7);
2326 void tst_qquicktextinput::cursorRectangle()
2329 QString text = "Hello World!";
2331 QQuickTextInput input;
2332 input.setText(text);
2333 input.componentComplete();
2335 QTextLayout layout(text);
2336 layout.setFont(input.font());
2337 if (!qmlDisableDistanceField()) {
2339 option.setUseDesignMetrics(true);
2340 layout.setTextOption(option);
2342 layout.beginLayout();
2343 QTextLine line = layout.createLine();
2346 input.setWidth(line.cursorToX(5, QTextLine::Leading));
2347 input.setHeight(qCeil(line.height() * 3 / 2));
2351 // some tolerance for different fonts.
2353 const int error = 2;
2355 const int error = 5;
2358 for (int i = 0; i <= 5; ++i) {
2359 input.setCursorPosition(i);
2360 r = input.cursorRectangle();
2362 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2363 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2364 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2367 // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2368 QVERIFY(r.left() < input.width());
2369 QVERIFY(r.right() >= input.width() - error);
2371 for (int i = 6; i < text.length(); ++i) {
2372 input.setCursorPosition(i);
2373 QCOMPARE(r, input.cursorRectangle());
2374 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2377 for (int i = text.length() - 2; i >= 0; --i) {
2378 input.setCursorPosition(i);
2379 r = input.cursorRectangle();
2380 QCOMPARE(r.top(), 0);
2381 QVERIFY(r.right() >= 0);
2382 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2385 // Check vertical scrolling with word wrap.
2386 input.setWrapMode(QQuickTextInput::WordWrap);
2387 for (int i = 0; i <= 5; ++i) {
2388 input.setCursorPosition(i);
2389 r = input.cursorRectangle();
2391 QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2392 QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2393 QCOMPARE(r.top(), 0);
2394 QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2397 input.setCursorPosition(6);
2398 r = input.cursorRectangle();
2399 QCOMPARE(r.left(), 0);
2400 QVERIFY(r.bottom() >= input.height() - error);
2402 for (int i = 7; i < text.length(); ++i) {
2403 input.setCursorPosition(i);
2404 r = input.cursorRectangle();
2405 QVERIFY(r.bottom() >= input.height() - error);
2408 for (int i = text.length() - 2; i >= 6; --i) {
2409 input.setCursorPosition(i);
2410 r = input.cursorRectangle();
2411 QVERIFY(r.bottom() >= input.height() - error);
2414 for (int i = 5; i >= 0; --i) {
2415 input.setCursorPosition(i);
2416 r = input.cursorRectangle();
2417 QCOMPARE(r.top(), 0);
2420 input.setText("Hi!");
2421 input.setHAlign(QQuickTextInput::AlignRight);
2422 r = input.cursorRectangle();
2423 QVERIFY(r.left() < input.width() + error);
2424 QVERIFY(r.right() >= input.width() - error);
2427 void tst_qquicktextinput::readOnly()
2429 QQuickView canvas(testFileUrl("readOnly.qml"));
2431 canvas.requestActivateWindow();
2433 QVERIFY(canvas.rootObject() != 0);
2435 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2437 QVERIFY(input != 0);
2438 QTRY_VERIFY(input->hasActiveFocus() == true);
2439 QVERIFY(input->isReadOnly() == true);
2440 QString initial = input->text();
2441 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2442 simulateKey(&canvas, k);
2443 simulateKey(&canvas, Qt::Key_Return);
2444 simulateKey(&canvas, Qt::Key_Space);
2445 simulateKey(&canvas, Qt::Key_Escape);
2446 QCOMPARE(input->text(), initial);
2448 input->setCursorPosition(3);
2449 input->setReadOnly(false);
2450 QCOMPARE(input->isReadOnly(), false);
2451 QCOMPARE(input->cursorPosition(), input->text().length());
2454 void tst_qquicktextinput::echoMode()
2456 QQuickView canvas(testFileUrl("echoMode.qml"));
2458 canvas.requestActivateWindow();
2459 QTest::qWaitForWindowShown(&canvas);
2460 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2462 QVERIFY(canvas.rootObject() != 0);
2464 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2466 QVERIFY(input != 0);
2467 QTRY_VERIFY(input->hasActiveFocus() == true);
2468 QString initial = input->text();
2469 Qt::InputMethodHints ref;
2470 QCOMPARE(initial, QLatin1String("ABCDefgh"));
2471 QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2472 QCOMPARE(input->displayText(), input->text());
2474 ref &= ~Qt::ImhHiddenText;
2475 ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2476 QCOMPARE(input->inputMethodHints(), ref);
2477 input->setEchoMode(QQuickTextInput::NoEcho);
2478 QCOMPARE(input->text(), initial);
2479 QCOMPARE(input->displayText(), QLatin1String(""));
2480 QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2482 ref |= Qt::ImhHiddenText;
2483 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2484 QCOMPARE(input->inputMethodHints(), ref);
2485 input->setEchoMode(QQuickTextInput::Password);
2487 ref |= Qt::ImhHiddenText;
2488 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2489 QCOMPARE(input->text(), initial);
2490 QCOMPARE(input->displayText(), QLatin1String("********"));
2491 QCOMPARE(input->inputMethodHints(), ref);
2492 input->setPasswordCharacter(QChar('Q'));
2493 QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2494 QCOMPARE(input->text(), initial);
2495 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2496 input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2497 //PasswordEchoOnEdit
2498 ref &= ~Qt::ImhHiddenText;
2499 ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2500 QCOMPARE(input->inputMethodHints(), ref);
2501 QCOMPARE(input->text(), initial);
2502 QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2503 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2504 QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2505 QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2506 QCOMPARE(input->text(), QLatin1String("a"));
2507 QCOMPARE(input->displayText(), QLatin1String("a"));
2508 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2509 input->setFocus(false);
2510 QVERIFY(input->hasActiveFocus() == false);
2511 QCOMPARE(input->displayText(), QLatin1String("Q"));
2512 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2513 input->setFocus(true);
2514 QVERIFY(input->hasActiveFocus());
2515 QInputMethodEvent inputEvent;
2516 inputEvent.setCommitString(initial);
2517 QGuiApplication::sendEvent(input, &inputEvent);
2518 QCOMPARE(input->text(), initial);
2519 QCOMPARE(input->displayText(), initial);
2520 QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2523 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2524 void tst_qquicktextinput::passwordEchoDelay()
2526 QQuickView canvas(testFileUrl("echoMode.qml"));
2528 canvas.requestActivateWindow();
2529 QTest::qWaitForWindowShown(&canvas);
2530 QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2532 QVERIFY(canvas.rootObject() != 0);
2534 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2536 QChar fillChar = QLatin1Char('*');
2538 input->setEchoMode(QQuickTextInput::Password);
2539 QCOMPARE(input->displayText(), QString(8, fillChar));
2540 input->setText(QString());
2541 QCOMPARE(input->displayText(), QString());
2543 QTest::keyPress(&canvas, '0');
2544 QTest::keyPress(&canvas, '1');
2545 QTest::keyPress(&canvas, '2');
2546 QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2547 QTest::keyPress(&canvas, '3');
2548 QTest::keyPress(&canvas, '4');
2549 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2550 QTest::keyPress(&canvas, Qt::Key_Backspace);
2551 QCOMPARE(input->displayText(), QString(4, fillChar));
2552 QTest::keyPress(&canvas, '4');
2553 QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2554 QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2555 QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2556 QTest::keyPress(&canvas, '5');
2557 QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2558 input->setFocus(false);
2559 QVERIFY(!input->hasFocus());
2560 QCOMPARE(input->displayText(), QString(6, fillChar));
2561 input->setFocus(true);
2562 QTRY_VERIFY(input->hasFocus());
2563 QCOMPARE(input->displayText(), QString(6, fillChar));
2564 QTest::keyPress(&canvas, '6');
2565 QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2567 QInputMethodEvent ev;
2568 ev.setCommitString(QLatin1String("7"));
2569 QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2570 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2572 input->setCursorPosition(3);
2573 QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2574 QTest::keyPress(&canvas, 'a');
2575 QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2576 QTest::keyPress(&canvas, Qt::Key_Backspace);
2577 QCOMPARE(input->displayText(), QString(8, fillChar));
2582 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2584 QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2585 QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2587 QGuiApplication::sendEvent(view, &press);
2588 QGuiApplication::sendEvent(view, &release);
2592 void tst_qquicktextinput::openInputPanel()
2594 PlatformInputContext platformInputContext;
2595 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2596 inputPanelPrivate->testContext = &platformInputContext;
2598 QQuickView view(testFileUrl("openInputPanel.qml"));
2600 view.requestActivateWindow();
2601 QTest::qWaitForWindowShown(&view);
2602 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2604 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2607 // check default values
2608 QVERIFY(input->focusOnPress());
2609 QVERIFY(!input->hasActiveFocus());
2610 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2611 QCOMPARE(qApp->inputPanel()->visible(), false);
2613 // input panel should open on focus
2614 QPoint centerPoint(view.width()/2, view.height()/2);
2615 Qt::KeyboardModifiers noModifiers = 0;
2616 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2617 QGuiApplication::processEvents();
2618 QVERIFY(input->hasActiveFocus());
2619 QCOMPARE(qApp->inputPanel()->inputItem(), input);
2620 QCOMPARE(qApp->inputPanel()->visible(), true);
2621 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2623 // input panel should be re-opened when pressing already focused TextInput
2624 qApp->inputPanel()->hide();
2625 QCOMPARE(qApp->inputPanel()->visible(), false);
2626 QVERIFY(input->hasActiveFocus());
2627 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2628 QGuiApplication::processEvents();
2629 QCOMPARE(qApp->inputPanel()->visible(), true);
2630 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2632 // input panel should stay visible if focus is lost to another text inputor
2633 QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2634 QQuickTextInput anotherInput;
2635 anotherInput.componentComplete();
2636 anotherInput.setParentItem(view.rootObject());
2637 anotherInput.setFocus(true);
2638 QCOMPARE(qApp->inputPanel()->visible(), true);
2639 QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2640 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2642 anotherInput.setFocus(false);
2643 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2644 QCOMPARE(view.activeFocusItem(), view.rootItem());
2645 anotherInput.setFocus(true);
2647 // input item should be null if focus is lost to an item that doesn't accept inputs
2649 item.setParentItem(view.rootObject());
2650 item.setFocus(true);
2651 QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2652 QCOMPARE(view.activeFocusItem(), &item);
2654 qApp->inputPanel()->hide();
2656 // input panel should not be opened if TextInput is read only
2657 input->setReadOnly(true);
2658 input->setFocus(true);
2659 QCOMPARE(qApp->inputPanel()->visible(), false);
2660 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2661 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2662 QGuiApplication::processEvents();
2663 QCOMPARE(qApp->inputPanel()->visible(), false);
2665 // input panel should not be opened if focusOnPress is set to false
2666 input->setFocusOnPress(false);
2667 input->setFocus(false);
2668 input->setFocus(true);
2669 QCOMPARE(qApp->inputPanel()->visible(), false);
2670 QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2671 QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2672 QCOMPARE(qApp->inputPanel()->visible(), false);
2674 // input panel should open when openSoftwareInputPanel is called
2675 input->openSoftwareInputPanel();
2676 QCOMPARE(qApp->inputPanel()->visible(), true);
2678 // input panel should close when closeSoftwareInputPanel is called
2679 input->closeSoftwareInputPanel();
2680 QCOMPARE(qApp->inputPanel()->visible(), false);
2683 class MyTextInput : public QQuickTextInput
2686 MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2690 virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2693 return QQuickTextInput::updatePaintNode(node, data);
2698 void tst_qquicktextinput::setHAlignClearCache()
2702 input.setText("Hello world");
2703 input.setParentItem(view.rootItem());
2705 view.requestActivateWindow();
2706 QTest::qWaitForWindowShown(&view);
2708 QEXPECT_FAIL("", "QTBUG-23485", Abort);
2710 QTRY_COMPARE(input.nbPaint, 1);
2711 input.setHAlign(QQuickTextInput::AlignRight);
2712 //Changing the alignment should trigger a repaint
2713 QTRY_COMPARE(input.nbPaint, 2);
2716 void tst_qquicktextinput::focusOutClearSelection()
2719 QQuickTextInput input;
2720 QQuickTextInput input2;
2721 input.setText(QLatin1String("Hello world"));
2722 input.setFocus(true);
2723 input2.setParentItem(view.rootItem());
2724 input.setParentItem(view.rootItem());
2725 input.componentComplete();
2726 input2.componentComplete();
2728 view.requestActivateWindow();
2729 QTest::qWaitForWindowShown(&view);
2731 //The selection should work
2732 QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2733 input2.setFocus(true);
2734 QGuiApplication::processEvents();
2735 //The input lost the focus selection should be cleared
2736 QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2739 void tst_qquicktextinput::geometrySignals()
2741 QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2742 QObject *o = component.create();
2744 QCOMPARE(o->property("bindingWidth").toInt(), 400);
2745 QCOMPARE(o->property("bindingHeight").toInt(), 500);
2749 void tst_qquicktextinput::testQtQuick11Attributes()
2751 QFETCH(QString, code);
2752 QFETCH(QString, warning);
2753 QFETCH(QString, error);
2755 QDeclarativeEngine engine;
2758 QDeclarativeComponent valid(&engine);
2759 valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2760 obj = valid.create();
2762 QVERIFY(valid.errorString().isEmpty());
2765 QDeclarativeComponent invalid(&engine);
2766 invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2767 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2768 obj = invalid.create();
2769 QCOMPARE(invalid.errorString(), error);
2773 void tst_qquicktextinput::testQtQuick11Attributes_data()
2775 QTest::addColumn<QString>("code");
2776 QTest::addColumn<QString>("warning");
2777 QTest::addColumn<QString>("error");
2779 QTest::newRow("canPaste") << "property bool foo: canPaste"
2780 << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2783 QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2784 << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2787 QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2788 << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2792 static void sendPreeditText(const QString &text, int cursor)
2794 QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2795 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2796 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2799 void tst_qquicktextinput::preeditAutoScroll()
2801 QString preeditText = "califragisiticexpialidocious!";
2803 QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2805 view.requestActivateWindow();
2806 QTest::qWaitForWindowShown(&view);
2807 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2808 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2810 QVERIFY(input->hasActiveFocus());
2812 input->setWidth(input->implicitWidth());
2814 QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2815 int cursorRectangleChanges = 0;
2817 // test the text is scrolled so the preedit is visible.
2818 sendPreeditText(preeditText.mid(0, 3), 1);
2819 QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2820 QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2821 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2823 // test the text is scrolled back when the preedit is removed.
2824 QInputMethodEvent imEvent;
2825 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2826 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2827 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2828 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2830 QTextLayout layout(preeditText);
2831 layout.setFont(input->font());
2832 if (!qmlDisableDistanceField()) {
2834 option.setUseDesignMetrics(true);
2835 layout.setTextOption(option);
2837 layout.beginLayout();
2838 QTextLine line = layout.createLine();
2841 // test if the preedit is larger than the text input that the
2842 // character preceding the cursor is still visible.
2843 qreal x = input->positionToRectangle(0).x();
2844 for (int i = 0; i < 3; ++i) {
2845 sendPreeditText(preeditText, i + 1);
2846 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2847 QVERIFY(input->cursorRectangle().right() >= width - 3);
2848 QVERIFY(input->positionToRectangle(0).x() < x);
2849 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2850 x = input->positionToRectangle(0).x();
2852 for (int i = 1; i >= 0; --i) {
2853 sendPreeditText(preeditText, i + 1);
2854 int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2855 QVERIFY(input->cursorRectangle().right() >= width - 3);
2856 QVERIFY(input->positionToRectangle(0).x() > x);
2857 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2858 x = input->positionToRectangle(0).x();
2861 // Test incrementing the preedit cursor doesn't cause further
2862 // scrolling when right most text is visible.
2863 sendPreeditText(preeditText, preeditText.length() - 3);
2864 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2865 x = input->positionToRectangle(0).x();
2866 for (int i = 2; i >= 0; --i) {
2867 sendPreeditText(preeditText, preeditText.length() - i);
2868 QCOMPARE(input->positionToRectangle(0).x(), x);
2869 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2871 for (int i = 1; i < 3; ++i) {
2872 sendPreeditText(preeditText, preeditText.length() - i);
2873 QCOMPARE(input->positionToRectangle(0).x(), x);
2874 QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2877 // Test disabling auto scroll.
2878 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2880 input->setAutoScroll(false);
2881 sendPreeditText(preeditText.mid(0, 3), 1);
2882 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2883 QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2886 void tst_qquicktextinput::preeditCursorRectangle()
2888 QString preeditText = "super";
2890 QQuickView view(testFileUrl("inputMethodEvent.qml"));
2892 view.requestActivateWindow();
2893 QTest::qWaitForWindowShown(&view);
2894 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2895 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2900 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2901 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2902 QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2904 // Verify that the micro focus rect is positioned the same for position 0 as
2905 // it would be if there was no preedit text.
2906 sendPreeditText(preeditText, 0);
2907 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2908 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2909 QCOMPARE(currentRect, previousRect);
2911 QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2912 QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2914 // Verify that the micro focus rect moves to the left as the cursor position
2916 for (int i = 1; i <= 5; ++i) {
2917 sendPreeditText(preeditText, i);
2918 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2919 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2920 QVERIFY(previousRect.left() < currentRect.left());
2921 QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2922 QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2923 previousRect = currentRect;
2926 // Verify that if there is no preedit cursor then the micro focus rect is the
2927 // same as it would be if it were positioned at the end of the preedit text.
2928 sendPreeditText(preeditText, 0);
2929 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2930 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2931 QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2932 currentRect = query.value(Qt::ImCursorRectangle).toRect();
2933 QCOMPARE(currentRect, previousRect);
2934 QVERIFY(inputSpy.count() > 0);
2935 QVERIFY(panelSpy.count() > 0);
2938 void tst_qquicktextinput::inputContextMouseHandler()
2940 PlatformInputContext platformInputContext;
2941 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2942 inputPanelPrivate->testContext = &platformInputContext;
2944 QString text = "supercalifragisiticexpialidocious!";
2945 QQuickView view(testFileUrl("inputContext.qml"));
2946 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2949 input->setFocus(true);
2953 view.requestActivateWindow();
2954 QTest::qWaitForWindowShown(&view);
2955 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2957 QTextLayout layout(text);
2958 layout.setFont(input->font());
2959 if (!qmlDisableDistanceField()) {
2961 option.setUseDesignMetrics(true);
2962 layout.setTextOption(option);
2964 layout.beginLayout();
2965 QTextLine line = layout.createLine();
2968 const qreal x = line.cursorToX(2, QTextLine::Leading);
2969 const qreal y = line.height() / 2;
2970 QPoint position = QPointF(x, y).toPoint();
2972 QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2973 QGuiApplication::sendEvent(input, &inputEvent);
2975 QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2976 QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2977 QGuiApplication::processEvents();
2979 QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2980 QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2981 QCOMPARE(platformInputContext.m_cursorPosition, 2);
2984 void tst_qquicktextinput::inputMethodComposing()
2986 QString text = "supercalifragisiticexpialidocious!";
2988 QQuickView view(testFileUrl("inputContext.qml"));
2990 view.requestActivateWindow();
2991 QTest::qWaitForWindowShown(&view);
2992 QTRY_COMPARE(&view, qGuiApp->focusWindow());
2993 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2995 QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2997 QCOMPARE(input->isInputMethodComposing(), false);
2999 QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3000 QGuiApplication::sendEvent(input, &event);
3002 QCOMPARE(input->isInputMethodComposing(), true);
3003 QCOMPARE(spy.count(), 1);
3006 QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3007 QGuiApplication::sendEvent(input, &event);
3009 QCOMPARE(spy.count(), 1);
3012 QInputMethodEvent event;
3013 QGuiApplication::sendEvent(input, &event);
3015 QCOMPARE(input->isInputMethodComposing(), false);
3016 QCOMPARE(spy.count(), 2);
3019 void tst_qquicktextinput::inputPanelUpdate()
3021 PlatformInputContext platformInputContext;
3022 QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
3023 inputPanelPrivate->testContext = &platformInputContext;
3025 QQuickView view(testFileUrl("inputContext.qml"));
3027 view.requestActivateWindow();
3028 QTest::qWaitForWindowShown(&view);
3029 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3030 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3033 // text change even without cursor position change needs to trigger update
3034 input->setText("test");
3035 platformInputContext.clear();
3036 input->setText("xxxx");
3037 QVERIFY(platformInputContext.m_updateCallCount > 0);
3039 // input method event replacing text
3040 platformInputContext.clear();
3042 QInputMethodEvent inputMethodEvent;
3043 inputMethodEvent.setCommitString("y", -1, 1);
3044 QGuiApplication::sendEvent(input, &inputMethodEvent);
3046 QVERIFY(platformInputContext.m_updateCallCount > 0);
3048 // input method changing selection
3049 platformInputContext.clear();
3051 QList<QInputMethodEvent::Attribute> attributes;
3052 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3053 QInputMethodEvent inputMethodEvent("", attributes);
3054 QGuiApplication::sendEvent(input, &inputMethodEvent);
3056 QVERIFY(input->selectionStart() != input->selectionEnd());
3057 QVERIFY(platformInputContext.m_updateCallCount > 0);
3059 // programmatical selections trigger update
3060 platformInputContext.clear();
3062 QVERIFY(platformInputContext.m_updateCallCount > 0);
3065 platformInputContext.clear();
3066 QFont font = input->font();
3067 font.setBold(!font.bold());
3068 input->setFont(font);
3069 QVERIFY(platformInputContext.m_updateCallCount > 0);
3072 platformInputContext.clear();
3074 QInputMethodEvent inputMethodEvent;
3075 inputMethodEvent.setCommitString("y");
3076 QGuiApplication::sendEvent(input, &inputMethodEvent);
3078 QVERIFY(platformInputContext.m_updateCallCount > 0);
3080 // changing cursor position
3081 platformInputContext.clear();
3082 input->setCursorPosition(0);
3083 QVERIFY(platformInputContext.m_updateCallCount > 0);
3086 void tst_qquicktextinput::cursorRectangleSize()
3088 QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3089 QVERIFY(canvas->rootObject() != 0);
3090 QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3092 // make sure cursor rectangle is not at (0,0)
3093 textInput->setX(10);
3094 textInput->setY(10);
3095 textInput->setCursorPosition(3);
3096 QVERIFY(textInput != 0);
3097 textInput->setFocus(true);
3099 canvas->requestActivateWindow();
3100 QTest::qWaitForWindowShown(canvas);
3102 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3103 qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
3104 QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3106 QRect cursorRectFromItem = textInput->cursorRectangle();
3107 QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3109 // item and input query cursor rectangles match
3110 QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
3112 // item cursor rectangle and positionToRectangle calculations match
3113 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
3115 // item-canvas transform and input item transform match
3116 QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
3118 // input panel cursorRectangle property and tranformed item cursor rectangle match
3119 QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3120 QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
3125 void tst_qquicktextinput::tripleClickSelectsAll()
3127 QString qmlfile = testFile("positionAt.qml");
3128 QQuickView view(QUrl::fromLocalFile(qmlfile));
3130 view.requestActivateWindow();
3131 QTest::qWaitForWindowShown(&view);
3133 QTRY_COMPARE(&view, qGuiApp->focusWindow());
3135 QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3138 QLatin1String hello("Hello world!");
3139 input->setSelectByMouse(true);
3140 input->setText(hello);
3142 // Clicking on the same point inside TextInput three times in a row
3143 // should trigger a triple click, thus selecting all the text.
3144 QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3145 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3146 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3147 QGuiApplication::processEvents();
3148 QCOMPARE(input->selectedText(), hello);
3150 // Now it simulates user moving the mouse between the second and the third click.
3151 // In this situation, we don't expect a triple click.
3152 QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3153 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3154 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3155 QGuiApplication::processEvents();
3156 QVERIFY(input->selectedText().isEmpty());
3158 // And now we press the third click too late, so no triple click event is triggered.
3159 QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3160 QGuiApplication::processEvents();
3161 QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3162 QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3163 QGuiApplication::processEvents();
3164 QVERIFY(input->selectedText().isEmpty());
3167 void tst_qquicktextinput::QTBUG_19956_data()
3169 QTest::addColumn<QString>("url");
3170 QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3171 QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3175 void tst_qquicktextinput::getText_data()
3177 QTest::addColumn<QString>("text");
3178 QTest::addColumn<QString>("inputMask");
3179 QTest::addColumn<int>("start");
3180 QTest::addColumn<int>("end");
3181 QTest::addColumn<QString>("expectedText");
3183 QTest::newRow("all plain text")
3186 << 0 << standard.at(0).length()
3189 QTest::newRow("plain text sub string")
3193 << standard.at(0).mid(0, 12);
3195 QTest::newRow("plain text sub string reversed")
3199 << standard.at(0).mid(0, 12);
3201 QTest::newRow("plain text cropped beginning")
3205 << standard.at(0).mid(0, 4);
3207 QTest::newRow("plain text cropped end")
3210 << 23 << standard.at(0).length() + 8
3211 << standard.at(0).mid(23);
3213 QTest::newRow("plain text cropped beginning and end")
3216 << -9 << standard.at(0).length() + 4
3220 void tst_qquicktextinput::getText()
3222 QFETCH(QString, text);
3223 QFETCH(QString, inputMask);
3226 QFETCH(QString, expectedText);
3228 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3229 QDeclarativeComponent textInputComponent(&engine);
3230 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3231 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3232 QVERIFY(textInput != 0);
3234 QCOMPARE(textInput->getText(start, end), expectedText);
3237 void tst_qquicktextinput::insert_data()
3239 QTest::addColumn<QString>("text");
3240 QTest::addColumn<QString>("inputMask");
3241 QTest::addColumn<int>("selectionStart");
3242 QTest::addColumn<int>("selectionEnd");
3243 QTest::addColumn<int>("insertPosition");
3244 QTest::addColumn<QString>("insertText");
3245 QTest::addColumn<QString>("expectedText");
3246 QTest::addColumn<int>("expectedSelectionStart");
3247 QTest::addColumn<int>("expectedSelectionEnd");
3248 QTest::addColumn<int>("expectedCursorPosition");
3249 QTest::addColumn<bool>("selectionChanged");
3250 QTest::addColumn<bool>("cursorPositionChanged");
3252 QTest::newRow("at cursor position (beginning)")
3257 << QString("Hello") + standard.at(0)
3261 QTest::newRow("at cursor position (end)")
3264 << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3266 << standard.at(0) + QString("Hello")
3267 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3270 QTest::newRow("at cursor position (middle)")
3275 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3279 QTest::newRow("after cursor position (beginning)")
3284 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3288 QTest::newRow("before cursor position (end)")
3291 << standard.at(0).length() << standard.at(0).length() << 18
3293 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3294 << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3297 QTest::newRow("before cursor position (middle)")
3302 << QString("Hello") + standard.at(0)
3306 QTest::newRow("after cursor position (middle)")
3309 << 18 << 18 << standard.at(0).length()
3311 << standard.at(0) + QString("Hello")
3315 QTest::newRow("before selection")
3320 << QString("Hello") + standard.at(0)
3324 QTest::newRow("before reversed selection")
3329 << QString("Hello") + standard.at(0)
3333 QTest::newRow("after selection")
3336 << 14 << 19 << standard.at(0).length()
3338 << standard.at(0) + QString("Hello")
3342 QTest::newRow("after reversed selection")
3345 << 19 << 14 << standard.at(0).length()
3347 << standard.at(0) + QString("Hello")
3351 QTest::newRow("into selection")
3356 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3360 QTest::newRow("into reversed selection")
3365 << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3369 QTest::newRow("rich text into plain text")
3373 << QString("<b>Hello</b>")
3374 << QString("<b>Hello</b>") + standard.at(0)
3378 QTest::newRow("before start")
3387 QTest::newRow("past end")
3390 << 0 << 0 << standard.at(0).length() + 3
3396 const QString inputMask = "009.009.009.009";
3397 const QString ip = "192.168.2.14";
3399 QTest::newRow("mask: at cursor position (beginning)")
3404 << QString("125.168.2.14")
3408 QTest::newRow("mask: at cursor position (end)")
3411 << inputMask.length() << inputMask.length() << inputMask.length()
3414 << inputMask.length() << inputMask.length() << inputMask.length()
3417 QTest::newRow("mask: at cursor position (middle)")
3422 << QString("192.167.5.24")
3426 QTest::newRow("mask: after cursor position (beginning)")
3431 << QString("192.167.5.24")
3435 QTest::newRow("mask: before cursor position (end)")
3438 << inputMask.length() << inputMask.length() << 6
3440 << QString("192.167.5.24")
3441 << inputMask.length() << inputMask.length() << inputMask.length()
3444 QTest::newRow("mask: before cursor position (middle)")
3449 << QString("125.168.2.14")
3453 QTest::newRow("mask: after cursor position (middle)")
3462 QTest::newRow("mask: before selection")
3467 << QString("125.168.2.14")
3471 QTest::newRow("mask: before reversed selection")
3476 << QString("125.168.2.14")
3480 QTest::newRow("mask: after selection")
3489 QTest::newRow("mask: after reversed selection")
3498 QTest::newRow("mask: into selection")
3503 << QString("192.167.5.24")
3507 QTest::newRow("mask: into reversed selection")
3512 << QString("192.167.5.24")
3516 QTest::newRow("mask: before start")
3525 QTest::newRow("mask: past end")
3528 << 0 << 0 << ip.length() + 3
3534 QTest::newRow("mask: invalid characters")
3539 << QString("192.168.2.14")
3543 QTest::newRow("mask: mixed validity")
3547 << QString("a1b2c5")
3548 << QString("125.168.2.14")
3553 void tst_qquicktextinput::insert()
3555 QFETCH(QString, text);
3556 QFETCH(QString, inputMask);
3557 QFETCH(int, selectionStart);
3558 QFETCH(int, selectionEnd);
3559 QFETCH(int, insertPosition);
3560 QFETCH(QString, insertText);
3561 QFETCH(QString, expectedText);
3562 QFETCH(int, expectedSelectionStart);
3563 QFETCH(int, expectedSelectionEnd);
3564 QFETCH(int, expectedCursorPosition);
3565 QFETCH(bool, selectionChanged);
3566 QFETCH(bool, cursorPositionChanged);
3568 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3569 QDeclarativeComponent textInputComponent(&engine);
3570 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3571 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3572 QVERIFY(textInput != 0);
3574 textInput->select(selectionStart, selectionEnd);
3576 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3577 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3578 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3579 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3580 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3582 textInput->insert(insertPosition, insertText);
3584 QCOMPARE(textInput->text(), expectedText);
3585 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3587 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3588 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3589 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3591 if (selectionStart > selectionEnd)
3592 qSwap(selectionStart, selectionEnd);
3594 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3595 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3596 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3597 QCOMPARE(textSpy.count() > 0, text != expectedText);
3598 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3601 void tst_qquicktextinput::remove_data()
3603 QTest::addColumn<QString>("text");
3604 QTest::addColumn<QString>("inputMask");
3605 QTest::addColumn<int>("selectionStart");
3606 QTest::addColumn<int>("selectionEnd");
3607 QTest::addColumn<int>("removeStart");
3608 QTest::addColumn<int>("removeEnd");
3609 QTest::addColumn<QString>("expectedText");
3610 QTest::addColumn<int>("expectedSelectionStart");
3611 QTest::addColumn<int>("expectedSelectionEnd");
3612 QTest::addColumn<int>("expectedCursorPosition");
3613 QTest::addColumn<bool>("selectionChanged");
3614 QTest::addColumn<bool>("cursorPositionChanged");
3616 QTest::newRow("from cursor position (beginning)")
3621 << standard.at(0).mid(5)
3625 QTest::newRow("to cursor position (beginning)")
3630 << standard.at(0).mid(5)
3634 QTest::newRow("to cursor position (end)")
3637 << standard.at(0).length() << standard.at(0).length()
3638 << standard.at(0).length() << standard.at(0).length() - 5
3639 << standard.at(0).mid(0, standard.at(0).length() - 5)
3640 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3643 QTest::newRow("to cursor position (end)")
3646 << standard.at(0).length() << standard.at(0).length()
3647 << standard.at(0).length() - 5 << standard.at(0).length()
3648 << standard.at(0).mid(0, standard.at(0).length() - 5)
3649 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3652 QTest::newRow("from cursor position (middle)")
3657 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3661 QTest::newRow("to cursor position (middle)")
3666 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3670 QTest::newRow("after cursor position (beginning)")
3675 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3679 QTest::newRow("before cursor position (end)")
3682 << standard.at(0).length() << standard.at(0).length()
3684 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3685 << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3688 QTest::newRow("before cursor position (middle)")
3693 << standard.at(0).mid(5)
3697 QTest::newRow("after cursor position (middle)")
3702 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3706 QTest::newRow("before selection")
3711 << standard.at(0).mid(5)
3715 QTest::newRow("before reversed selection")
3720 << standard.at(0).mid(5)
3724 QTest::newRow("after selection")
3728 << standard.at(0).length() - 5 << standard.at(0).length()
3729 << standard.at(0).mid(0, standard.at(0).length() - 5)
3733 QTest::newRow("after reversed selection")
3737 << standard.at(0).length() - 5 << standard.at(0).length()
3738 << standard.at(0).mid(0, standard.at(0).length() - 5)
3742 QTest::newRow("from selection")
3747 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3751 QTest::newRow("from reversed selection")
3756 << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3760 QTest::newRow("cropped beginning")
3765 << standard.at(0).mid(4)
3769 QTest::newRow("cropped end")
3773 << 23 << standard.at(0).length() + 8
3774 << standard.at(0).mid(0, 23)
3778 QTest::newRow("cropped beginning and end")
3782 << -9 << standard.at(0).length() + 4
3787 const QString inputMask = "009.009.009.009";
3788 const QString ip = "192.168.2.14";
3790 QTest::newRow("mask: from cursor position")
3795 << QString("192.16..14")
3799 QTest::newRow("mask: to cursor position")
3804 << QString("19.8.2.14")
3808 QTest::newRow("mask: before cursor position")
3813 << QString("2.168.2.14")
3817 QTest::newRow("mask: after cursor position")
3822 << QString("192.168.2.")
3826 QTest::newRow("mask: before selection")
3831 << QString("2.168.2.14")
3835 QTest::newRow("mask: before reversed selection")
3840 << QString("2.168.2.14")
3844 QTest::newRow("mask: after selection")
3849 << QString("192.168.2.")
3853 QTest::newRow("mask: after reversed selection")
3858 << QString("192.168.2.")
3862 QTest::newRow("mask: from selection")
3867 << QString("192.168..14")
3871 QTest::newRow("mask: from reversed selection")
3876 << QString("192.168..14")
3880 QTest::newRow("mask: cropped beginning")
3885 << QString(".168.2.14")
3889 QTest::newRow("mask: cropped end")
3894 << QString("192.168.2.1")
3898 QTest::newRow("mask: cropped beginning and end")
3908 void tst_qquicktextinput::remove()
3910 QFETCH(QString, text);
3911 QFETCH(QString, inputMask);
3912 QFETCH(int, selectionStart);
3913 QFETCH(int, selectionEnd);
3914 QFETCH(int, removeStart);
3915 QFETCH(int, removeEnd);
3916 QFETCH(QString, expectedText);
3917 QFETCH(int, expectedSelectionStart);
3918 QFETCH(int, expectedSelectionEnd);
3919 QFETCH(int, expectedCursorPosition);
3920 QFETCH(bool, selectionChanged);
3921 QFETCH(bool, cursorPositionChanged);
3923 QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3924 QDeclarativeComponent textInputComponent(&engine);
3925 textInputComponent.setData(componentStr.toLatin1(), QUrl());
3926 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3927 QVERIFY(textInput != 0);
3929 textInput->select(selectionStart, selectionEnd);
3931 QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3932 QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3933 QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3934 QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3935 QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3937 textInput->remove(removeStart, removeEnd);
3939 QCOMPARE(textInput->text(), expectedText);
3940 QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3942 if (selectionStart > selectionEnd) //
3943 qSwap(selectionStart, selectionEnd);
3945 QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3946 QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3947 QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3949 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3950 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3951 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3952 QCOMPARE(textSpy.count() > 0, text != expectedText);
3954 if (cursorPositionChanged) //
3955 QVERIFY(cursorPositionSpy.count() > 0);
3958 void tst_qquicktextinput::keySequence_data()
3960 QTest::addColumn<QString>("text");
3961 QTest::addColumn<QKeySequence>("sequence");
3962 QTest::addColumn<int>("selectionStart");
3963 QTest::addColumn<int>("selectionEnd");
3964 QTest::addColumn<int>("cursorPosition");
3965 QTest::addColumn<QString>("expectedText");
3966 QTest::addColumn<QString>("selectedText");
3968 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3970 QTest::newRow("select all")
3971 << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3972 << 44 << standard.at(0) << standard.at(0);
3973 QTest::newRow("select end of line")
3974 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3975 << 44 << standard.at(0) << standard.at(0).mid(5);
3976 QTest::newRow("select end of document") // ### Not handled.
3977 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3978 << 3 << standard.at(0) << QString();
3979 QTest::newRow("select end of block")
3980 << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3981 << 44 << standard.at(0) << standard.at(0).mid(18);
3982 QTest::newRow("delete end of line")
3983 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3984 << 24 << standard.at(0).mid(0, 24) << QString();
3985 QTest::newRow("move to start of line")
3986 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3987 << 0 << standard.at(0) << QString();
3988 QTest::newRow("move to start of block")
3989 << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3990 << 0 << standard.at(0) << QString();
3991 QTest::newRow("move to next char")
3992 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3993 << 13 << standard.at(0) << QString();
3994 QTest::newRow("move to previous char")
3995 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3996 << 2 << standard.at(0) << QString();
3997 QTest::newRow("select next char")
3998 << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3999 << 24 << standard.at(0) << standard.at(0).mid(23, 1);
4000 QTest::newRow("select previous char")
4001 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4002 << 18 << standard.at(0) << standard.at(0).mid(18, 1);
4003 QTest::newRow("move to next word")
4004 << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4005 << 10 << standard.at(0) << QString();
4006 QTest::newRow("move to previous word")
4007 << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4008 << 4 << standard.at(0) << QString();
4009 QTest::newRow("select previous word")
4010 << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4011 << 10 << standard.at(0) << standard.at(0).mid(10, 1);
4012 QTest::newRow("delete (selection)")
4013 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4014 << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
4015 QTest::newRow("delete (no selection)")
4016 << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4017 << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
4018 QTest::newRow("delete end of word")
4019 << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4020 << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
4021 QTest::newRow("delete start of word")
4022 << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4023 << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
4026 void tst_qquicktextinput::keySequence()
4028 QFETCH(QString, text);
4029 QFETCH(QKeySequence, sequence);
4030 QFETCH(int, selectionStart);
4031 QFETCH(int, selectionEnd);
4032 QFETCH(int, cursorPosition);
4033 QFETCH(QString, expectedText);
4034 QFETCH(QString, selectedText);
4036 if (sequence.isEmpty()) {
4037 QSKIP("Key sequence is undefined");
4040 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4041 QDeclarativeComponent textInputComponent(&engine);
4042 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4043 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4044 QVERIFY(textInput != 0);
4046 QQuickCanvas canvas;
4047 textInput->setParentItem(canvas.rootItem());
4049 canvas.requestActivateWindow();
4050 QTest::qWaitForWindowShown(&canvas);
4051 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4053 textInput->select(selectionStart, selectionEnd);
4055 simulateKeys(&canvas, sequence);
4057 QCOMPARE(textInput->cursorPosition(), cursorPosition);
4058 QCOMPARE(textInput->text(), expectedText);
4059 QCOMPARE(textInput->selectedText(), selectedText);
4063 #define REPLACE_UNTIL_END 1
4065 void tst_qquicktextinput::undo_data()
4067 QTest::addColumn<QStringList>("insertString");
4068 QTest::addColumn<IntList>("insertIndex");
4069 QTest::addColumn<IntList>("insertMode");
4070 QTest::addColumn<QStringList>("expectedString");
4071 QTest::addColumn<bool>("use_keys");
4073 for (int i=0; i<2; i++) {
4074 QString keys_str = "keyboard";
4075 bool use_keys = true;
4077 keys_str = "insert";
4082 IntList insertIndex;
4084 QStringList insertString;
4085 QStringList expectedString;
4088 insertMode << NORMAL;
4089 insertString << "1";
4092 insertMode << NORMAL;
4093 insertString << "5";
4096 insertMode << NORMAL;
4097 insertString << "3";
4100 insertMode << NORMAL;
4101 insertString << "2";
4104 insertMode << NORMAL;
4105 insertString << "4";
4107 expectedString << "12345";
4108 expectedString << "1235";
4109 expectedString << "135";
4110 expectedString << "15";
4111 expectedString << "";
4113 QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4121 IntList insertIndex;
4123 QStringList insertString;
4124 QStringList expectedString;
4127 insertMode << NORMAL;
4128 insertString << "World"; // World
4131 insertMode << NORMAL;
4132 insertString << "Hello"; // HelloWorld
4135 insertMode << NORMAL;
4136 insertString << "Well"; // WellHelloWorld
4139 insertMode << NORMAL;
4140 insertString << "There"; // WellHelloThereWorld;
4142 expectedString << "WellHelloThereWorld";
4143 expectedString << "WellHelloWorld";
4144 expectedString << "HelloWorld";
4145 expectedString << "World";
4146 expectedString << "";
4148 QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4156 IntList insertIndex;
4158 QStringList insertString;
4159 QStringList expectedString;
4162 insertMode << NORMAL;
4163 insertString << "Ensuring";
4166 insertMode << NORMAL;
4167 insertString << " instan";
4170 insertMode << NORMAL;
4171 insertString << "an ";
4174 insertMode << REPLACE_UNTIL_END;
4175 insertString << " unique instance.";
4177 expectedString << "Ensuring a unique instance.";
4178 expectedString << "Ensuring an instan";
4179 expectedString << "Ensuring instan";
4180 expectedString << "";
4182 QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4192 void tst_qquicktextinput::undo()
4194 QFETCH(QStringList, insertString);
4195 QFETCH(IntList, insertIndex);
4196 QFETCH(IntList, insertMode);
4197 QFETCH(QStringList, expectedString);
4199 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4200 QDeclarativeComponent textInputComponent(&engine);
4201 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4202 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4203 QVERIFY(textInput != 0);
4205 QQuickCanvas canvas;
4206 textInput->setParentItem(canvas.rootItem());
4208 canvas.requestActivateWindow();
4209 QTest::qWaitForWindowShown(&canvas);
4210 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4212 QVERIFY(!textInput->canUndo());
4214 QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4218 // STEP 1: First build up an undo history by inserting or typing some strings...
4219 for (i = 0; i < insertString.size(); ++i) {
4220 if (insertIndex[i] > -1)
4221 textInput->setCursorPosition(insertIndex[i]);
4223 // experimental stuff
4224 if (insertMode[i] == REPLACE_UNTIL_END) {
4225 textInput->select(insertIndex[i], insertIndex[i] + 8);
4227 // This is what I actually want...
4228 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4231 for (int j = 0; j < insertString.at(i).length(); j++)
4232 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4235 QCOMPARE(spy.count(), 1);
4237 // STEP 2: Next call undo several times and see if we can restore to the previous state
4238 for (i = 0; i < expectedString.size() - 1; ++i) {
4239 QCOMPARE(textInput->text(), expectedString[i]);
4240 QVERIFY(textInput->canUndo());
4244 // STEP 3: Verify that we have undone everything
4245 QVERIFY(textInput->text().isEmpty());
4246 QVERIFY(!textInput->canUndo());
4247 QCOMPARE(spy.count(), 2);
4250 void tst_qquicktextinput::redo_data()
4252 QTest::addColumn<QStringList>("insertString");
4253 QTest::addColumn<IntList>("insertIndex");
4254 QTest::addColumn<QStringList>("expectedString");
4257 IntList insertIndex;
4258 QStringList insertString;
4259 QStringList expectedString;
4262 insertString << "World"; // World
4264 insertString << "Hello"; // HelloWorld
4266 insertString << "Well"; // WellHelloWorld
4268 insertString << "There"; // WellHelloThereWorld;
4270 expectedString << "World";
4271 expectedString << "HelloWorld";
4272 expectedString << "WellHelloWorld";
4273 expectedString << "WellHelloThereWorld";
4275 QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4279 void tst_qquicktextinput::redo()
4281 QFETCH(QStringList, insertString);
4282 QFETCH(IntList, insertIndex);
4283 QFETCH(QStringList, expectedString);
4285 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4286 QDeclarativeComponent textInputComponent(&engine);
4287 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4288 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4289 QVERIFY(textInput != 0);
4291 QQuickCanvas canvas;
4292 textInput->setParentItem(canvas.rootItem());
4294 canvas.requestActivateWindow();
4295 QTest::qWaitForWindowShown(&canvas);
4296 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4298 QVERIFY(!textInput->canUndo());
4299 QVERIFY(!textInput->canRedo());
4301 QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
4304 // inserts the diff strings at diff positions
4305 for (i = 0; i < insertString.size(); ++i) {
4306 if (insertIndex[i] > -1)
4307 textInput->setCursorPosition(insertIndex[i]);
4308 for (int j = 0; j < insertString.at(i).length(); j++)
4309 QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4310 QVERIFY(textInput->canUndo());
4311 QVERIFY(!textInput->canRedo());
4314 QCOMPARE(spy.count(), 0);
4317 while (!textInput->text().isEmpty()) {
4318 QVERIFY(textInput->canUndo());
4320 QVERIFY(textInput->canRedo());
4323 QCOMPARE(spy.count(), 1);
4325 for (i = 0; i < expectedString.size(); ++i) {
4326 QVERIFY(textInput->canRedo());
4328 QCOMPARE(textInput->text() , expectedString[i]);
4329 QVERIFY(textInput->canUndo());
4331 QVERIFY(!textInput->canRedo());
4332 QCOMPARE(spy.count(), 2);
4335 void tst_qquicktextinput::undo_keypressevents_data()
4337 QTest::addColumn<KeyList>("keys");
4338 QTest::addColumn<QStringList>("expectedString");
4342 QStringList expectedString;
4345 << QKeySequence::MoveToStartOfLine
4352 << QKeySequence::MoveToEndOfLine
4355 expectedString << "BEVERYAFRAID!";
4356 expectedString << "BEVERYAFRAID";
4357 expectedString << "VERYAFRAID";
4358 expectedString << "AFRAID";
4360 QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4363 QStringList expectedString;
4366 keys << "1234" << QKeySequence::MoveToStartOfLine
4368 << Qt::Key_Right << Qt::Key_Right
4370 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4374 expectedString << "12";
4375 expectedString << "1234";
4377 QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4380 QStringList expectedString;
4384 << QKeySequence::MoveToStartOfLine
4386 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4388 << QKeySequence::Undo
4390 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
4393 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4396 expectedString << "AB";
4397 expectedString << "AB12";
4399 QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4402 QStringList expectedString;
4407 << Qt::Key_Left << Qt::Key_Left
4411 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4412 // overwriting '1234' with '5'
4414 // undoing deletion of 'AB'
4415 << QKeySequence::Undo
4416 // overwriting '1234' with '6'
4419 expectedString << "ab6cd";
4420 // for versions previous to 3.2 we overwrite needed two undo operations
4421 expectedString << "ab1234cd";
4422 expectedString << "abcd";
4424 QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4427 QStringList expectedString;
4432 << Qt::Key_Backspace;
4434 expectedString << "AB";
4435 expectedString << "ABC";
4437 QTest::newRow("Inserts,backspace") << keys << expectedString;
4440 QStringList expectedString;
4444 << Qt::Key_Backspace
4448 expectedString << "ABZ";
4449 expectedString << "AB";
4450 expectedString << "ABC";
4452 QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4455 QStringList expectedString;
4458 keys << "123" << QKeySequence::MoveToStartOfLine
4460 << QKeySequence::SelectEndOfLine
4461 // overwriting '123' with 'ABC'
4464 expectedString << "ABC";
4465 // for versions previous to 3.2 we overwrite needed two undo operations
4466 expectedString << "123";
4468 QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4472 void tst_qquicktextinput::undo_keypressevents()
4474 QFETCH(KeyList, keys);
4475 QFETCH(QStringList, expectedString);
4477 QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4478 QDeclarativeComponent textInputComponent(&engine);
4479 textInputComponent.setData(componentStr.toLatin1(), QUrl());
4480 QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4481 QVERIFY(textInput != 0);
4483 QQuickCanvas canvas;
4484 textInput->setParentItem(canvas.rootItem());
4486 canvas.requestActivateWindow();
4487 QTest::qWaitForWindowShown(&canvas);
4488 QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4490 simulateKeys(&canvas, keys);
4492 for (int i = 0; i < expectedString.size(); ++i) {
4493 QCOMPARE(textInput->text() , expectedString[i]);
4496 QVERIFY(textInput->text().isEmpty());
4499 void tst_qquicktextinput::QTBUG_19956()
4501 QFETCH(QString, url);
4503 QQuickView canvas(testFileUrl(url));
4505 canvas.requestActivateWindow();
4506 QTest::qWaitForWindowShown(&canvas);
4507 QVERIFY(canvas.rootObject() != 0);
4508 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4510 input->setFocus(true);
4511 QVERIFY(input->hasActiveFocus());
4513 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
4514 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4515 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
4516 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4518 canvas.rootObject()->setProperty("topvalue", 15);
4519 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
4520 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4522 canvas.rootObject()->setProperty("topvalue", 25);
4523 QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
4524 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4526 canvas.rootObject()->setProperty("bottomvalue", 21);
4527 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
4528 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4530 canvas.rootObject()->setProperty("bottomvalue", 10);
4531 QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4532 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4535 void tst_qquicktextinput::QTBUG_19956_regexp()
4537 QUrl url = testFileUrl("qtbug-19956regexp.qml");
4539 QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
4540 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
4542 QQuickView canvas(url);
4544 canvas.requestActivateWindow();
4545 QTest::qWaitForWindowShown(&canvas);
4546 QVERIFY(canvas.rootObject() != 0);
4547 QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4549 input->setFocus(true);
4550 QVERIFY(input->hasActiveFocus());
4552 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4553 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4554 QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
4555 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4557 canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
4558 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
4559 QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4561 canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4562 QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4563 QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4567 void tst_qquicktextinput::negativeDimensions()
4569 // Verify this doesn't assert during initialization.
4570 QDeclarativeComponent component(&engine, testFileUrl("negativeDimensions.qml"));
4571 QScopedPointer<QObject> o(component.create());
4573 QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
4575 QCOMPARE(input->width(), qreal(-1));
4576 QCOMPARE(input->height(), qreal(-1));
4579 QTEST_MAIN(tst_qquicktextinput)
4581 #include "tst_qquicktextinput.moc"