d2d8d5ad14dd90d157332a3bb631b79fbcf59bad
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquicktextinput / tst_qquicktextinput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/util.h"
44 #include <private/qinputmethod_p.h>
45 #include <QtQml/qqmlengine.h>
46 #include <QtQml/qqmlexpression.h>
47 #include <QFile>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputMethod>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
54 #include <QDebug>
55 #include <QDir>
56 #include <math.h>
57
58 #ifdef Q_OS_MAC
59 #include <Carbon/Carbon.h>
60 #endif
61
62 #include "qplatformdefs.h"
63 #include "../../shared/platforminputcontext.h"
64
65 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
66 Q_DECLARE_METATYPE(QQuickTextInput::EchoMode)
67 Q_DECLARE_METATYPE(Qt::Key)
68
69 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
70
71 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
72 {
73     // XXX This will be replaced by some clever persistent platform image store.
74     QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
75     QString arch = "unknown-architecture"; // QTest needs to help with this.
76
77     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
78
79     if (!QFile::exists(expectfile)) {
80         actual.save(expectfile);
81         qWarning() << "created" << expectfile;
82     }
83
84     return expectfile;
85 }
86
87 template <typename T> static T evaluate(QObject *scope, const QString &expression)
88 {
89     QQmlExpression expr(qmlContext(scope), scope, expression);
90     T result = expr.evaluate().value<T>();
91     if (expr.hasError())
92         qWarning() << expr.error().toString();
93     return result;
94 }
95
96 template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
97
98 typedef QPair<int, QChar> Key;
99
100 class tst_qquicktextinput : public QQmlDataTest
101
102 {
103     Q_OBJECT
104 public:
105     tst_qquicktextinput();
106
107 private slots:
108     void cleanup();
109     void text();
110     void width();
111     void font();
112     void color();
113     void wrap();
114     void selection();
115     void persistentSelection();
116     void isRightToLeft_data();
117     void isRightToLeft();
118     void moveCursorSelection_data();
119     void moveCursorSelection();
120     void moveCursorSelectionSequence_data();
121     void moveCursorSelectionSequence();
122     void dragMouseSelection();
123     void mouseSelectionMode_data();
124     void mouseSelectionMode();
125     void tripleClickSelectsAll();
126
127     void horizontalAlignment_data();
128     void horizontalAlignment();
129     void horizontalAlignment_RightToLeft();
130     void verticalAlignment();
131
132     void clipRect();
133     void boundingRect();
134
135     void positionAt();
136
137     void maxLength();
138     void masks();
139     void validators();
140     void inputMethods();
141
142     void passwordCharacter();
143     void cursorDelegate_data();
144     void cursorDelegate();
145     void cursorVisible();
146     void cursorRectangle_data();
147     void cursorRectangle();
148     void navigation();
149     void navigation_RTL();
150     void copyAndPaste();
151     void copyAndPasteKeySequence();
152     void canPasteEmpty();
153     void canPaste();
154     void readOnly();
155     void focusOnPress();
156
157     void openInputPanel();
158     void setHAlignClearCache();
159     void focusOutClearSelection();
160
161     void echoMode();
162     void passwordEchoDelay();
163     void geometrySignals();
164     void contentSize();
165
166     void preeditAutoScroll();
167     void preeditCursorRectangle();
168     void inputContextMouseHandler();
169     void inputMethodComposing();
170     void inputMethodUpdate();
171     void cursorRectangleSize();
172
173     void getText_data();
174     void getText();
175     void insert_data();
176     void insert();
177     void remove_data();
178     void remove();
179
180     void keySequence_data();
181     void keySequence();
182
183     void undo_data();
184     void undo();
185     void redo_data();
186     void redo();
187     void undo_keypressevents_data();
188     void undo_keypressevents();
189
190     void QTBUG_19956();
191     void QTBUG_19956_data();
192     void QTBUG_19956_regexp();
193
194     void implicitSize_data();
195     void implicitSize();
196     void implicitSizeBinding_data();
197     void implicitSizeBinding();
198
199     void negativeDimensions();
200
201
202     void setInputMask_data();
203     void setInputMask();
204     void inputMask_data();
205     void inputMask();
206     void clearInputMask();
207     void keypress_inputMask_data();
208     void keypress_inputMask();
209     void hasAcceptableInputMask_data();
210     void hasAcceptableInputMask();
211     void maskCharacter_data();
212     void maskCharacter();
213
214 private:
215     void simulateKey(QWindow *, int key);
216
217     void simulateKeys(QWindow *window, const QList<Key> &keys);
218     void simulateKeys(QWindow *window, const QKeySequence &sequence);
219
220     QQmlEngine engine;
221     QStringList standard;
222     QStringList colorStrings;
223 };
224
225 typedef QList<int> IntList;
226 Q_DECLARE_METATYPE(IntList)
227
228 typedef QList<Key> KeyList;
229 Q_DECLARE_METATYPE(KeyList)
230
231 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
232 {
233     for (int i = 0; i < keys.count(); ++i) {
234         const int key = keys.at(i).first;
235         const int modifiers = key & Qt::KeyboardModifierMask;
236         const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
237
238         QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
239         QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
240
241         QGuiApplication::sendEvent(window, &press);
242         QGuiApplication::sendEvent(window, &release);
243     }
244 }
245
246 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
247 {
248     for (int i = 0; i < sequence.count(); ++i) {
249         const int key = sequence[i];
250         const int modifiers = key & Qt::KeyboardModifierMask;
251
252         QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
253     }
254 }
255
256 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
257 {
258     for (int i = 0; i < sequence.count(); ++i)
259         keys << Key(sequence[i], QChar());
260     return keys;
261 }
262
263 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
264 {
265     for (int i = 0; i < N - 1; ++i) {
266         int key = QTest::asciiToKey(characters[i]);
267         QChar character = QLatin1Char(characters[i]);
268         keys << Key(key, character);
269     }
270     return keys;
271 }
272
273 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
274 {
275     keys << Key(key, QChar());
276     return keys;
277 }
278
279 void tst_qquicktextinput::cleanup()
280 {
281     // ensure not even skipped tests with custom input context leave it dangling
282     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
283     inputMethodPrivate->testContext = 0;
284 }
285
286 tst_qquicktextinput::tst_qquicktextinput()
287 {
288     standard << "the quick brown fox jumped over the lazy dog"
289         << "It's supercalifragisiticexpialidocious!"
290         << "Hello, world!"
291         << "!dlrow ,olleH"
292         << " spacey   text ";
293
294     colorStrings << "aliceblue"
295                  << "antiquewhite"
296                  << "aqua"
297                  << "darkkhaki"
298                  << "darkolivegreen"
299                  << "dimgray"
300                  << "palevioletred"
301                  << "lightsteelblue"
302                  << "#000000"
303                  << "#AAAAAA"
304                  << "#FFFFFF"
305                  << "#2AC05F";
306 }
307
308 void tst_qquicktextinput::text()
309 {
310     {
311         QQmlComponent textinputComponent(&engine);
312         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\"  }", QUrl());
313         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
314
315         QVERIFY(textinputObject != 0);
316         QCOMPARE(textinputObject->text(), QString(""));
317         QCOMPARE(textinputObject->length(), 0);
318
319         delete textinputObject;
320     }
321
322     for (int i = 0; i < standard.size(); i++)
323     {
324         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
325         QQmlComponent textinputComponent(&engine);
326         textinputComponent.setData(componentStr.toLatin1(), QUrl());
327         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
328
329         QVERIFY(textinputObject != 0);
330         QCOMPARE(textinputObject->text(), standard.at(i));
331         QCOMPARE(textinputObject->length(), standard.at(i).length());
332
333         delete textinputObject;
334     }
335
336 }
337
338 void tst_qquicktextinput::width()
339 {
340     // uses Font metrics to find the width for standard
341     {
342         QQmlComponent textinputComponent(&engine);
343         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\" }", QUrl());
344         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
345
346         QVERIFY(textinputObject != 0);
347         QCOMPARE(textinputObject->width(), 0.0);
348
349         delete textinputObject;
350     }
351
352     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
353
354     for (int i = 0; i < standard.size(); i++)
355     {
356         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
357         QQmlComponent textinputComponent(&engine);
358         textinputComponent.setData(componentStr.toLatin1(), QUrl());
359         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
360
361         QString s = standard.at(i);
362         s.replace(QLatin1Char('\n'), QChar::LineSeparator);
363
364         QTextLayout layout(s);
365         layout.setFont(textinputObject->font());
366         layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
367         if (requiresUnhintedMetrics) {
368             QTextOption option;
369             option.setUseDesignMetrics(true);
370             layout.setTextOption(option);
371         }
372
373         layout.beginLayout();
374         forever {
375             QTextLine line = layout.createLine();
376             if (!line.isValid())
377                 break;
378         }
379
380         layout.endLayout();
381
382         qreal metricWidth = ceil(layout.boundingRect().width());
383
384         QVERIFY(textinputObject != 0);
385         int delta = abs(int(int(textinputObject->width()) - metricWidth));
386         QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
387
388         delete textinputObject;
389     }
390 }
391
392 void tst_qquicktextinput::font()
393 {
394     //test size, then bold, then italic, then family
395     {
396         QString componentStr = "import QtQuick 2.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
397         QQmlComponent textinputComponent(&engine);
398         textinputComponent.setData(componentStr.toLatin1(), QUrl());
399         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
400
401         QVERIFY(textinputObject != 0);
402         QCOMPARE(textinputObject->font().pointSize(), 40);
403         QCOMPARE(textinputObject->font().bold(), false);
404         QCOMPARE(textinputObject->font().italic(), false);
405
406         delete textinputObject;
407     }
408
409     {
410         QString componentStr = "import QtQuick 2.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
411         QQmlComponent textinputComponent(&engine);
412         textinputComponent.setData(componentStr.toLatin1(), QUrl());
413         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
414
415         QVERIFY(textinputObject != 0);
416         QCOMPARE(textinputObject->font().bold(), true);
417         QCOMPARE(textinputObject->font().italic(), false);
418
419         delete textinputObject;
420     }
421
422     {
423         QString componentStr = "import QtQuick 2.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
424         QQmlComponent textinputComponent(&engine);
425         textinputComponent.setData(componentStr.toLatin1(), QUrl());
426         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
427
428         QVERIFY(textinputObject != 0);
429         QCOMPARE(textinputObject->font().italic(), true);
430         QCOMPARE(textinputObject->font().bold(), false);
431
432         delete textinputObject;
433     }
434
435     {
436         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
437         QQmlComponent textinputComponent(&engine);
438         textinputComponent.setData(componentStr.toLatin1(), QUrl());
439         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
440
441         QVERIFY(textinputObject != 0);
442         QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
443         QCOMPARE(textinputObject->font().bold(), false);
444         QCOMPARE(textinputObject->font().italic(), false);
445
446         delete textinputObject;
447     }
448
449     {
450         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
451         QQmlComponent textinputComponent(&engine);
452         textinputComponent.setData(componentStr.toLatin1(), QUrl());
453         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
454
455         QVERIFY(textinputObject != 0);
456         QCOMPARE(textinputObject->font().family(), QString(""));
457
458         delete textinputObject;
459     }
460 }
461
462 void tst_qquicktextinput::color()
463 {
464     //test color
465     for (int i = 0; i < colorStrings.size(); i++)
466     {
467         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
468         QQmlComponent textinputComponent(&engine);
469         textinputComponent.setData(componentStr.toLatin1(), QUrl());
470         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
471         QVERIFY(textinputObject != 0);
472         QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
473
474         delete textinputObject;
475     }
476
477     //test selection color
478     for (int i = 0; i < colorStrings.size(); i++)
479     {
480         QString componentStr = "import QtQuick 2.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
481         QQmlComponent textinputComponent(&engine);
482         textinputComponent.setData(componentStr.toLatin1(), QUrl());
483         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
484         QVERIFY(textinputObject != 0);
485         QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
486
487         delete textinputObject;
488     }
489
490     //test selected text color
491     for (int i = 0; i < colorStrings.size(); i++)
492     {
493         QString componentStr = "import QtQuick 2.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
494         QQmlComponent textinputComponent(&engine);
495         textinputComponent.setData(componentStr.toLatin1(), QUrl());
496         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
497         QVERIFY(textinputObject != 0);
498         QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
499
500         delete textinputObject;
501     }
502
503     {
504         QString colorStr = "#AA001234";
505         QColor testColor("#001234");
506         testColor.setAlpha(170);
507
508         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
509         QQmlComponent textinputComponent(&engine);
510         textinputComponent.setData(componentStr.toLatin1(), QUrl());
511         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
512
513         QVERIFY(textinputObject != 0);
514         QCOMPARE(textinputObject->color(), testColor);
515
516         delete textinputObject;
517     }
518 }
519
520 void tst_qquicktextinput::wrap()
521 {
522     int textHeight = 0;
523     // for specified width and wrap set true
524     {
525         QQmlComponent textComponent(&engine);
526         textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
527         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
528         textHeight = textObject->height();
529
530         QVERIFY(textObject != 0);
531         QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
532         QCOMPARE(textObject->width(), 300.);
533
534         delete textObject;
535     }
536
537     for (int i = 0; i < standard.count(); i++) {
538         QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
539         QQmlComponent textComponent(&engine);
540         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
541         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
542
543         QVERIFY(textObject != 0);
544         QCOMPARE(textObject->width(), 30.);
545         QVERIFY(textObject->height() > textHeight);
546
547         int oldHeight = textObject->height();
548         textObject->setWidth(100);
549         QVERIFY(textObject->height() < oldHeight);
550
551         delete textObject;
552     }
553 }
554
555 void tst_qquicktextinput::selection()
556 {
557     QString testStr = standard[0];
558     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
559     QQmlComponent textinputComponent(&engine);
560     textinputComponent.setData(componentStr.toLatin1(), QUrl());
561     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
562     QVERIFY(textinputObject != 0);
563
564
565     //Test selection follows cursor
566     for (int i=0; i<= testStr.size(); i++) {
567         textinputObject->setCursorPosition(i);
568         QCOMPARE(textinputObject->cursorPosition(), i);
569         QCOMPARE(textinputObject->selectionStart(), i);
570         QCOMPARE(textinputObject->selectionEnd(), i);
571         QVERIFY(textinputObject->selectedText().isNull());
572     }
573
574     textinputObject->setCursorPosition(0);
575     QVERIFY(textinputObject->cursorPosition() == 0);
576     QVERIFY(textinputObject->selectionStart() == 0);
577     QVERIFY(textinputObject->selectionEnd() == 0);
578     QVERIFY(textinputObject->selectedText().isNull());
579
580     // Verify invalid positions are ignored.
581     textinputObject->setCursorPosition(-1);
582     QVERIFY(textinputObject->cursorPosition() == 0);
583     QVERIFY(textinputObject->selectionStart() == 0);
584     QVERIFY(textinputObject->selectionEnd() == 0);
585     QVERIFY(textinputObject->selectedText().isNull());
586
587     textinputObject->setCursorPosition(textinputObject->text().count()+1);
588     QVERIFY(textinputObject->cursorPosition() == 0);
589     QVERIFY(textinputObject->selectionStart() == 0);
590     QVERIFY(textinputObject->selectionEnd() == 0);
591     QVERIFY(textinputObject->selectedText().isNull());
592
593     //Test selection
594     for (int i=0; i<= testStr.size(); i++) {
595         textinputObject->select(0,i);
596         QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
597     }
598     for (int i=0; i<= testStr.size(); i++) {
599         textinputObject->select(i,testStr.size());
600         QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
601     }
602
603     textinputObject->setCursorPosition(0);
604     QVERIFY(textinputObject->cursorPosition() == 0);
605     QVERIFY(textinputObject->selectionStart() == 0);
606     QVERIFY(textinputObject->selectionEnd() == 0);
607     QVERIFY(textinputObject->selectedText().isNull());
608
609     //Test Error Ignoring behaviour
610     textinputObject->setCursorPosition(0);
611     QVERIFY(textinputObject->selectedText().isNull());
612     textinputObject->select(-10,0);
613     QVERIFY(textinputObject->selectedText().isNull());
614     textinputObject->select(100,110);
615     QVERIFY(textinputObject->selectedText().isNull());
616     textinputObject->select(0,-10);
617     QVERIFY(textinputObject->selectedText().isNull());
618     textinputObject->select(0,100);
619     QVERIFY(textinputObject->selectedText().isNull());
620     textinputObject->select(0,10);
621     QVERIFY(textinputObject->selectedText().size() == 10);
622     textinputObject->select(-10,10);
623     QVERIFY(textinputObject->selectedText().size() == 10);
624     textinputObject->select(100,101);
625     QVERIFY(textinputObject->selectedText().size() == 10);
626     textinputObject->select(0,-10);
627     QVERIFY(textinputObject->selectedText().size() == 10);
628     textinputObject->select(0,100);
629     QVERIFY(textinputObject->selectedText().size() == 10);
630
631     textinputObject->deselect();
632     QVERIFY(textinputObject->selectedText().isNull());
633     textinputObject->select(0,10);
634     QVERIFY(textinputObject->selectedText().size() == 10);
635     textinputObject->deselect();
636     QVERIFY(textinputObject->selectedText().isNull());
637
638     // test input method selection
639     QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
640     textinputObject->setFocus(true);
641     {
642         QList<QInputMethodEvent::Attribute> attributes;
643         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
644         QInputMethodEvent event("", attributes);
645         QGuiApplication::sendEvent(textinputObject, &event);
646     }
647     QCOMPARE(selectionSpy.count(), 1);
648     QCOMPARE(textinputObject->selectionStart(), 12);
649     QCOMPARE(textinputObject->selectionEnd(), 17);
650
651     delete textinputObject;
652 }
653
654 void tst_qquicktextinput::persistentSelection()
655 {
656     QQuickView canvas(testFileUrl("persistentSelection.qml"));
657     canvas.show();
658     canvas.requestActivateWindow();
659     QTest::qWaitForWindowShown(&canvas);
660     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
661     canvas.requestActivateWindow();
662
663     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
664     QVERIFY(input);
665     QVERIFY(input->hasActiveFocus());
666
667     QSignalSpy spy(input, SIGNAL(persistentSelectionChanged()));
668
669     QCOMPARE(input->persistentSelection(), false);
670
671     input->setPersistentSelection(false);
672     QCOMPARE(input->persistentSelection(), false);
673     QCOMPARE(spy.count(), 0);
674
675     input->select(1, 4);
676     QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
677
678     input->setFocus(false);
679     QCOMPARE(input->property("selected").toString(), QString());
680
681     input->setFocus(true);
682     QCOMPARE(input->property("selected").toString(), QString());
683
684     input->setPersistentSelection(true);
685     QCOMPARE(input->persistentSelection(), true);
686     QCOMPARE(spy.count(), 1);
687
688     input->select(1, 4);
689     QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
690
691     input->setFocus(false);
692     QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
693
694     input->setFocus(true);
695     QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
696 }
697
698 void tst_qquicktextinput::isRightToLeft_data()
699 {
700     QTest::addColumn<QString>("text");
701     QTest::addColumn<bool>("emptyString");
702     QTest::addColumn<bool>("firstCharacter");
703     QTest::addColumn<bool>("lastCharacter");
704     QTest::addColumn<bool>("middleCharacter");
705     QTest::addColumn<bool>("startString");
706     QTest::addColumn<bool>("midString");
707     QTest::addColumn<bool>("endString");
708
709     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
710     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
711     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
712     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
713     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
714     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;
715     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
716 }
717
718 void tst_qquicktextinput::isRightToLeft()
719 {
720     QFETCH(QString, text);
721     QFETCH(bool, emptyString);
722     QFETCH(bool, firstCharacter);
723     QFETCH(bool, lastCharacter);
724     QFETCH(bool, middleCharacter);
725     QFETCH(bool, startString);
726     QFETCH(bool, midString);
727     QFETCH(bool, endString);
728
729     QQuickTextInput textInput;
730     textInput.setText(text);
731
732     // first test that the right string is delivered to the QString::isRightToLeft()
733     QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
734     QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
735     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
736     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
737     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
738     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
739     if (text.isEmpty())
740         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
741     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
742
743     // then test that the feature actually works
744     QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
745     QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
746     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
747     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
748     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
749     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
750     if (text.isEmpty())
751         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
752     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
753 }
754
755 void tst_qquicktextinput::moveCursorSelection_data()
756 {
757     QTest::addColumn<QString>("testStr");
758     QTest::addColumn<int>("cursorPosition");
759     QTest::addColumn<int>("movePosition");
760     QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
761     QTest::addColumn<int>("selectionStart");
762     QTest::addColumn<int>("selectionEnd");
763     QTest::addColumn<bool>("reversible");
764
765     // () contains the text selected by the cursor.
766     // <> contains the actual selection.
767
768     QTest::newRow("(t)he|characters")
769             << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
770     QTest::newRow("do(g)|characters")
771             << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
772     QTest::newRow("jum(p)ed|characters")
773             << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
774     QTest::newRow("jumped( )over|characters")
775             << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
776     QTest::newRow("(the )|characters")
777             << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
778     QTest::newRow("( dog)|characters")
779             << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
780     QTest::newRow("( jumped )|characters")
781             << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
782     QTest::newRow("th(e qu)ick|characters")
783             << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
784     QTest::newRow("la(zy d)og|characters")
785             << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
786     QTest::newRow("jum(ped ov)er|characters")
787             << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
788     QTest::newRow("()the|characters")
789             << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
790     QTest::newRow("dog()|characters")
791             << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
792     QTest::newRow("jum()ped|characters")
793             << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
794
795     QTest::newRow("<(t)he>|words")
796             << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
797     QTest::newRow("<do(g)>|words")
798             << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
799     QTest::newRow("<jum(p)ed>|words")
800             << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
801     QTest::newRow("<jumped( )>over|words,ltr")
802             << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
803     QTest::newRow("jumped<( )over>|words,rtl")
804             << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
805     QTest::newRow("<(the )>quick|words,ltr")
806             << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
807     QTest::newRow("<(the )quick>|words,rtl")
808             << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
809     QTest::newRow("<lazy( dog)>|words,ltr")
810             << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
811     QTest::newRow("lazy<( dog)>|words,rtl")
812             << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
813     QTest::newRow("<fox( jumped )>over|words,ltr")
814             << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
815     QTest::newRow("fox<( jumped )over>|words,rtl")
816             << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
817     QTest::newRow("<th(e qu)ick>|words")
818             << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
819     QTest::newRow("<la(zy d)og|words>")
820             << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
821     QTest::newRow("<jum(ped ov)er>|words")
822             << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
823     QTest::newRow("<()>the|words")
824             << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
825     QTest::newRow("dog<()>|words")
826             << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
827     QTest::newRow("jum<()>ped|words")
828             << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
829
830     QTest::newRow("Hello<(,)> |words")
831             << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
832     QTest::newRow("Hello<(, )>world|words,ltr")
833             << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
834     QTest::newRow("Hello<(, )world>|words,rtl")
835             << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
836     QTest::newRow("<Hel(lo, )>world|words,ltr")
837             << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
838     QTest::newRow("<Hel(lo, )world>|words,rtl")
839             << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
840     QTest::newRow("<Hel(lo)>,|words")
841             << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
842     QTest::newRow("Hello<()>,|words")
843             << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
844     QTest::newRow("Hello,<()>|words")
845             << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
846     QTest::newRow("Hello<,( )>world|words,ltr")
847             << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
848     QTest::newRow("Hello,<( )world>|words,rtl")
849             << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
850     QTest::newRow("Hello<,( world)>|words,ltr")
851             << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
852     QTest::newRow("Hello,<( world)>|words,rtl")
853             << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
854     QTest::newRow("Hello<,( world!)>|words,ltr")
855             << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
856     QTest::newRow("Hello,<( world!)>|words,rtl")
857             << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
858     QTest::newRow("Hello<(, world!)>|words")
859             << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
860     // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
861     // QTBUG-11365
862     // QTest::newRow("world<(!)>|words")
863     //         << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
864     QTest::newRow("world!<()>)|words")
865             << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
866     QTest::newRow("world<()>!)|words")
867             << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
868
869     QTest::newRow("<(,)>olleH |words")
870             << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
871     QTest::newRow("<dlrow( ,)>olleH|words,ltr")
872             << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
873     QTest::newRow("dlrow<( ,)>olleH|words,rtl")
874             << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
875     QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
876             << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
877     QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
878             << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
879     QTest::newRow(",<(ol)leH>,|words")
880             << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
881     QTest::newRow(",<()>olleH|words")
882             << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
883     QTest::newRow("<()>,olleH|words")
884             << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
885     QTest::newRow("<dlrow( )>,olleH|words,ltr")
886             << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
887     QTest::newRow("dlrow<( ),>olleH|words,rtl")
888             << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
889     QTest::newRow("<(dlrow )>,olleH|words,ltr")
890             << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
891     QTest::newRow("<(dlrow ),>olleH|words,rtl")
892             << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
893     QTest::newRow("<(!dlrow )>,olleH|words,ltr")
894             << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
895     QTest::newRow("<(!dlrow ),>olleH|words,rtl")
896             << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
897     QTest::newRow("(!dlrow ,)olleH|words")
898             << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
899     QTest::newRow("<(!)>dlrow|words")
900             << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
901     QTest::newRow("<()>!dlrow|words")
902             << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
903     QTest::newRow("!<()>dlrow|words")
904             << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
905
906     QTest::newRow(" <s(pac)ey>   text |words")
907             << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
908     QTest::newRow(" spacey   <t(ex)t> |words")
909             << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
910     QTest::newRow("<( )>spacey   text |words|ltr")
911             << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
912     QTest::newRow("<( )spacey>   text |words|rtl")
913             << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
914     QTest::newRow("spacey   <text( )>|words|ltr")
915             << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
916 //    QTBUG-11365
917 //    QTest::newRow("spacey   text<( )>|words|rtl")
918 //            << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
919     QTest::newRow("<()> spacey   text |words")
920             << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
921     QTest::newRow(" spacey   text <()>|words")
922             << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
923 }
924
925 void tst_qquicktextinput::moveCursorSelection()
926 {
927     QFETCH(QString, testStr);
928     QFETCH(int, cursorPosition);
929     QFETCH(int, movePosition);
930     QFETCH(QQuickTextInput::SelectionMode, mode);
931     QFETCH(int, selectionStart);
932     QFETCH(int, selectionEnd);
933     QFETCH(bool, reversible);
934
935     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
936     QQmlComponent textinputComponent(&engine);
937     textinputComponent.setData(componentStr.toLatin1(), QUrl());
938     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
939     QVERIFY(textinputObject != 0);
940
941     textinputObject->setCursorPosition(cursorPosition);
942     textinputObject->moveCursorSelection(movePosition, mode);
943
944     QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
945     QCOMPARE(textinputObject->selectionStart(), selectionStart);
946     QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
947
948     if (reversible) {
949         textinputObject->setCursorPosition(movePosition);
950         textinputObject->moveCursorSelection(cursorPosition, mode);
951
952         QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
953         QCOMPARE(textinputObject->selectionStart(), selectionStart);
954         QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
955     }
956
957     delete textinputObject;
958 }
959
960 void tst_qquicktextinput::moveCursorSelectionSequence_data()
961 {
962     QTest::addColumn<QString>("testStr");
963     QTest::addColumn<int>("cursorPosition");
964     QTest::addColumn<int>("movePosition1");
965     QTest::addColumn<int>("movePosition2");
966     QTest::addColumn<int>("selection1Start");
967     QTest::addColumn<int>("selection1End");
968     QTest::addColumn<int>("selection2Start");
969     QTest::addColumn<int>("selection2End");
970
971     // () contains the text selected by the cursor.
972     // <> contains the actual selection.
973     // ^ is the revised cursor position.
974     // {} contains the revised selection.
975
976     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
977             << standard[0]
978             << 9 << 13 << 17
979             << 4 << 15
980             << 4 << 19;
981     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
982             << standard[0]
983             << 13 << 9 << 17
984             << 9 << 15
985             << 10 << 19;
986     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
987             << standard[0]
988             << 9 << 13 << 16
989             << 4 << 15
990             << 4 << 16;
991     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
992             << standard[0]
993             << 13 << 9 << 16
994             << 9 << 15
995             << 10 << 16;
996     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
997             << standard[0]
998             << 9 << 13 << 15
999             << 4 << 15
1000             << 4 << 15;
1001     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1002             << standard[0]
1003             << 13 << 9 << 15
1004             << 9 << 15
1005             << 10 << 15;
1006     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1007             << standard[0]
1008             << 9 << 13 << 10
1009             << 4 << 15
1010             << 4 << 10;
1011     QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
1012             << standard[0]
1013             << 13 << 9 << 10
1014             << 9 << 15
1015             << 10 << 15;
1016     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1017             << standard[0]
1018             << 9 << 13 << 9
1019             << 4 << 15
1020             << 4 << 9;
1021     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1022             << standard[0]
1023             << 13 << 9 << 9
1024             << 9 << 15
1025             << 9 << 15;
1026     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1027             << standard[0]
1028             << 9 << 13 << 7
1029             << 4 << 15
1030             << 4 << 9;
1031     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1032             << standard[0]
1033             << 13 << 9 << 7
1034             << 9 << 15
1035             << 4 << 15;
1036     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1037             << standard[0]
1038             << 9 << 13 << 4
1039             << 4 << 15
1040             << 4 << 9;
1041     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1042             << standard[0]
1043             << 13 << 9 << 4
1044             << 9 << 15
1045             << 4 << 15;
1046     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1047             << standard[0]
1048             << 9 << 13 << 3
1049             << 4 << 15
1050             << 3 << 9;
1051     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1052             << standard[0]
1053             << 13 << 9 << 3
1054             << 9 << 15
1055             << 3 << 15;
1056     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1057             << standard[0]
1058             << 9 << 13 << 1
1059             << 4 << 15
1060             << 0 << 9;
1061     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1062             << standard[0]
1063             << 13 << 9 << 1
1064             << 9 << 15
1065             << 0 << 15;
1066
1067     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1068             << standard[2]
1069             << 2 << 4 << 8
1070             << 0 << 5
1071             << 0 << 12;
1072     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1073             << standard[2]
1074             << 4 << 2 << 8
1075             << 0 << 5
1076             << 0 << 12;
1077
1078     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1079             << standard[3]
1080             << 9 << 11 << 5
1081             << 8 << 13
1082             << 1 << 13;
1083     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1084             << standard[3]
1085             << 11 << 9 << 5
1086             << 8 << 13
1087             << 1 << 13;
1088
1089     QTest::newRow("{<(^} sp)acey>   text |ltr")
1090             << standard[4]
1091             << 0 << 3 << 0
1092             << 0 << 7
1093             << 0 << 0;
1094     QTest::newRow("{<( ^}sp)acey>   text |ltr")
1095             << standard[4]
1096             << 0 << 3 << 1
1097             << 0 << 7
1098             << 0 << 1;
1099     QTest::newRow("<( {s^p)acey>}   text |rtl")
1100             << standard[4]
1101             << 3 << 0 << 2
1102             << 0 << 7
1103             << 1 << 7;
1104     QTest::newRow("<( {^sp)acey>}   text |rtl")
1105             << standard[4]
1106             << 3 << 0 << 1
1107             << 0 << 7
1108             << 1 << 7;
1109
1110     QTest::newRow(" spacey   <te(xt {^)>}|rtl")
1111             << standard[4]
1112             << 15 << 12 << 15
1113             << 10 << 15
1114             << 15 << 15;
1115 //    QTBUG-11365
1116 //    QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
1117 //            << standard[4]
1118 //            << 15 << 12 << 14
1119 //            << 10 << 15
1120 //            << 14 << 15;
1121     QTest::newRow(" spacey   {<te(x^t} )>|ltr")
1122             << standard[4]
1123             << 12 << 15 << 13
1124             << 10 << 15
1125             << 10 << 14;
1126 //    QTBUG-11365
1127 //    QTest::newRow(" spacey   {<te(xt^} )>|ltr")
1128 //            << standard[4]
1129 //            << 12 << 15 << 14
1130 //            << 10 << 15
1131 //            << 10 << 14;
1132 }
1133
1134 void tst_qquicktextinput::moveCursorSelectionSequence()
1135 {
1136     QFETCH(QString, testStr);
1137     QFETCH(int, cursorPosition);
1138     QFETCH(int, movePosition1);
1139     QFETCH(int, movePosition2);
1140     QFETCH(int, selection1Start);
1141     QFETCH(int, selection1End);
1142     QFETCH(int, selection2Start);
1143     QFETCH(int, selection2End);
1144
1145     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
1146     QQmlComponent textinputComponent(&engine);
1147     textinputComponent.setData(componentStr.toLatin1(), QUrl());
1148     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1149     QVERIFY(textinputObject != 0);
1150
1151     textinputObject->setCursorPosition(cursorPosition);
1152
1153     textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1154     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1155     QCOMPARE(textinputObject->selectionStart(), selection1Start);
1156     QCOMPARE(textinputObject->selectionEnd(), selection1End);
1157
1158     textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1159     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1160     QCOMPARE(textinputObject->selectionStart(), selection2Start);
1161     QCOMPARE(textinputObject->selectionEnd(), selection2End);
1162
1163     delete textinputObject;
1164 }
1165
1166 void tst_qquicktextinput::dragMouseSelection()
1167 {
1168     QString qmlfile = testFile("mouseselection_true.qml");
1169
1170     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1171
1172     canvas.show();
1173     canvas.requestActivateWindow();
1174     QTest::qWaitForWindowShown(&canvas);
1175
1176     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1177
1178     QVERIFY(canvas.rootObject() != 0);
1179     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1180     QVERIFY(textInputObject != 0);
1181
1182     // press-and-drag-and-release from x1 to x2
1183     int x1 = 10;
1184     int x2 = 70;
1185     int y = textInputObject->height()/2;
1186     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1187     QTest::mouseMove(&canvas, QPoint(x2, y));
1188     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1189     QTest::qWait(100);
1190     QString str1;
1191     QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1192     QVERIFY(str1.length() > 3);
1193
1194     // press and drag the current selection.
1195     x1 = 40;
1196     x2 = 100;
1197     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1198     QTest::mouseMove(&canvas, QPoint(x2, y));
1199     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1200     QTest::qWait(300);
1201     QString str2 = textInputObject->selectedText();
1202     QVERIFY(str2.length() > 3);
1203
1204     QVERIFY(str1 != str2);
1205 }
1206
1207 void tst_qquicktextinput::mouseSelectionMode_data()
1208 {
1209     QTest::addColumn<QString>("qmlfile");
1210     QTest::addColumn<bool>("selectWords");
1211     QTest::addColumn<bool>("focus");
1212     QTest::addColumn<bool>("focusOnPress");
1213
1214     // import installed
1215     QTest::newRow("SelectWords focused") << testFile("mouseselectionmode_words.qml") << true << true << true;
1216     QTest::newRow("SelectCharacters focused") << testFile("mouseselectionmode_characters.qml") << false << true << true;
1217     QTest::newRow("default focused") << testFile("mouseselectionmode_default.qml") << false << true << true;
1218     QTest::newRow("SelectWords unfocused") << testFile("mouseselectionmode_words.qml") << true << false << false;
1219     QTest::newRow("SelectCharacters unfocused") << testFile("mouseselectionmode_characters.qml") << false << false << false;
1220     QTest::newRow("default unfocused") << testFile("mouseselectionmode_default.qml") << false << true << false;
1221     QTest::newRow("SelectWords focuss on press") << testFile("mouseselectionmode_words.qml") << true << false << true;
1222     QTest::newRow("SelectCharacters focus on press") << testFile("mouseselectionmode_characters.qml") << false << false << true;
1223     QTest::newRow("default focus on press") << testFile("mouseselectionmode_default.qml") << false << false << true;
1224 }
1225
1226 void tst_qquicktextinput::mouseSelectionMode()
1227 {
1228     QFETCH(QString, qmlfile);
1229     QFETCH(bool, selectWords);
1230     QFETCH(bool, focus);
1231     QFETCH(bool, focusOnPress);
1232
1233     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1234
1235     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1236
1237     canvas.show();
1238     canvas.requestActivateWindow();
1239     QTest::qWaitForWindowShown(&canvas);
1240     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1241
1242     QVERIFY(canvas.rootObject() != 0);
1243     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1244     QVERIFY(textInputObject != 0);
1245
1246     textInputObject->setFocus(focus);
1247     textInputObject->setFocusOnPress(focusOnPress);
1248
1249     // press-and-drag-and-release from x1 to x2
1250     int x1 = 10;
1251     int x2 = 70;
1252     int y = textInputObject->height()/2;
1253     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1254     QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1255     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1256     QTest::qWait(300);
1257     if (selectWords) {
1258         QTRY_COMPARE(textInputObject->selectedText(), text);
1259     } else {
1260         QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1261         QVERIFY(textInputObject->selectedText() != text);
1262     }
1263 }
1264
1265 void tst_qquicktextinput::horizontalAlignment_data()
1266 {
1267     QTest::addColumn<int>("hAlign");
1268     QTest::addColumn<QString>("expectfile");
1269
1270     QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1271     QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1272     QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1273 }
1274
1275 void tst_qquicktextinput::horizontalAlignment()
1276 {
1277     QSKIP("Image comparison of text is almost guaranteed to fail during development");
1278
1279     QFETCH(int, hAlign);
1280     QFETCH(QString, expectfile);
1281
1282     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1283
1284     canvas.show();
1285     canvas.requestActivateWindow();
1286     QTest::qWaitForWindowShown(&canvas);
1287     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1288     QObject *ob = canvas.rootObject();
1289     QVERIFY(ob != 0);
1290     ob->setProperty("horizontalAlignment",hAlign);
1291     QImage actual = canvas.grabFrameBuffer();
1292
1293     expectfile = createExpectedFileIfNotFound(expectfile, actual);
1294
1295     QImage expect(expectfile);
1296
1297     QCOMPARE(actual,expect);
1298 }
1299
1300 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1301 {
1302     PlatformInputContext platformInputContext;
1303     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
1304     inputMethodPrivate->testContext = &platformInputContext;
1305
1306     QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1307     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1308     QVERIFY(textInput != 0);
1309     canvas.show();
1310
1311     const QString rtlText = textInput->text();
1312
1313     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1314     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1315
1316     // implicit alignment should follow the reading direction of RTL text
1317     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1318     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1319     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1320     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1321
1322     // explicitly left aligned
1323     textInput->setHAlign(QQuickTextInput::AlignLeft);
1324     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1325     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1326     QCOMPARE(textInput->boundingRect().left(), qreal(0));
1327
1328     // explicitly right aligned
1329     textInput->setHAlign(QQuickTextInput::AlignRight);
1330     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1331     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1332     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1333     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1334
1335     // explicitly center aligned
1336     textInput->setHAlign(QQuickTextInput::AlignHCenter);
1337     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1338     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1339     QVERIFY(textInput->boundingRect().left() > 0);
1340     QVERIFY(textInput->boundingRect().right() < textInput->width());
1341
1342     // reseted alignment should go back to following the text reading direction
1343     textInput->resetHAlign();
1344     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1345     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1346     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1347     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1348
1349     // mirror the text item
1350     QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1351
1352     // mirrored implicit alignment should continue to follow the reading direction of the text
1353     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1354     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1355     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1356     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1357
1358     // explicitly right aligned behaves as left aligned
1359     textInput->setHAlign(QQuickTextInput::AlignRight);
1360     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1361     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1362     QCOMPARE(textInput->boundingRect().left(), qreal(0));
1363
1364     // mirrored explicitly left aligned behaves as right aligned
1365     textInput->setHAlign(QQuickTextInput::AlignLeft);
1366     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1367     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1368     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1369     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1370
1371     // disable mirroring
1372     QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1373     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1374     textInput->resetHAlign();
1375
1376     // English text should be implicitly left aligned
1377     textInput->setText("Hello world!");
1378     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1379     QCOMPARE(textInput->boundingRect().left(), qreal(0));
1380
1381     canvas.requestActivateWindow();
1382     QTest::qWaitForWindowShown(&canvas);
1383     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1384
1385     // If there is no commited text, the preedit text should determine the alignment.
1386     textInput->setText(QString());
1387     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1388     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1389     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1390     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1391
1392     // Clear pre-edit text.  TextInput should maybe do this itself on setText, but that may be
1393     // redundant as an actual input method may take care of it.
1394     { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev); }
1395
1396     // empty text with implicit alignment follows the system locale-based
1397     // keyboard input direction from QInputMethod::inputDirection()
1398     textInput->setText("");
1399     platformInputContext.setInputDirection(Qt::LeftToRight);
1400     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
1401     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1402     QCOMPARE(textInput->boundingRect().left(), qreal(0));
1403
1404     QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1405     platformInputContext.setInputDirection(Qt::RightToLeft);
1406     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
1407     QCOMPARE(cursorRectangleSpy.count(), 1);
1408     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1409     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1410     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1411
1412     // set input direction while having content
1413     platformInputContext.setInputDirection(Qt::LeftToRight);
1414     textInput->setText("a");
1415     platformInputContext.setInputDirection(Qt::RightToLeft);
1416     QTest::keyClick(&canvas, Qt::Key_Backspace);
1417     QVERIFY(textInput->text().isEmpty());
1418     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1419     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1420     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1421
1422     // input direction changed while not having focus
1423     platformInputContext.setInputDirection(Qt::LeftToRight);
1424     textInput->setFocus(false);
1425     platformInputContext.setInputDirection(Qt::RightToLeft);
1426     textInput->setFocus(true);
1427     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1428     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1429     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1430
1431     textInput->setHAlign(QQuickTextInput::AlignRight);
1432     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1433     QVERIFY(textInput->boundingRect().right() >= textInput->width() - 1);
1434     QVERIFY(textInput->boundingRect().right() <= textInput->width() + 1);
1435
1436     // neutral text should fall back to input direction
1437     textInput->setFocus(true);
1438     textInput->resetHAlign();
1439     textInput->setText(" ()((=<>");
1440     platformInputContext.setInputDirection(Qt::LeftToRight);
1441     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1442     platformInputContext.setInputDirection(Qt::RightToLeft);
1443     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1444
1445     // changing width keeps right aligned cursor on proper position
1446     textInput->setText("");
1447     textInput->setWidth(500);
1448     QVERIFY(textInput->positionToRectangle(0).x() > textInput->width() / 2);
1449 }
1450
1451 void tst_qquicktextinput::verticalAlignment()
1452 {
1453     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1454     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1455     QVERIFY(textInput != 0);
1456     canvas.show();
1457
1458     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1459     QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1460     QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
1461     QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
1462
1463     // bottom aligned
1464     textInput->setVAlign(QQuickTextInput::AlignBottom);
1465     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1466     QVERIFY(textInput->boundingRect().top() > canvas.height() / 2);
1467     QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
1468     QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
1469
1470     // explicitly center aligned
1471     textInput->setVAlign(QQuickTextInput::AlignVCenter);
1472     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1473     QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1474     QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1475     QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
1476     QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
1477     QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
1478     QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
1479 }
1480
1481 void tst_qquicktextinput::clipRect()
1482 {
1483     QQmlComponent component(&engine);
1484     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1485     QScopedPointer<QObject> object(component.create());
1486     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1487     QVERIFY(input);
1488
1489     QCOMPARE(input->clipRect().x(), qreal(0));
1490     QCOMPARE(input->clipRect().y(), qreal(0));
1491     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1492     QCOMPARE(input->clipRect().height(), input->height());
1493
1494     input->setText("Hello World");
1495     QCOMPARE(input->clipRect().x(), qreal(0));
1496     QCOMPARE(input->clipRect().y(), qreal(0));
1497     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1498     QCOMPARE(input->clipRect().height(), input->height());
1499
1500     // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1501     input->setWidth(input->width() / 2);
1502     QCOMPARE(input->clipRect().x(), qreal(0));
1503     QCOMPARE(input->clipRect().y(), qreal(0));
1504     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1505     QCOMPARE(input->clipRect().height(), input->height());
1506
1507     input->setHeight(input->height() * 2);
1508     QCOMPARE(input->clipRect().x(), qreal(0));
1509     QCOMPARE(input->clipRect().y(), qreal(0));
1510     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1511     QCOMPARE(input->clipRect().height(), input->height());
1512
1513     QQmlComponent cursorComponent(&engine);
1514     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1515
1516     input->setCursorDelegate(&cursorComponent);
1517     input->setCursorVisible(true);
1518
1519     // If a cursor delegate is used it's size should determine the excess width.
1520     QCOMPARE(input->clipRect().x(), qreal(0));
1521     QCOMPARE(input->clipRect().y(), qreal(0));
1522     QCOMPARE(input->clipRect().width(), input->width() + 8);
1523     QCOMPARE(input->clipRect().height(), input->height());
1524
1525     // Alignment, auto scroll, wrapping all don't affect the clip rect.
1526     input->setAutoScroll(false);
1527     QCOMPARE(input->clipRect().x(), qreal(0));
1528     QCOMPARE(input->clipRect().y(), qreal(0));
1529     QCOMPARE(input->clipRect().width(), input->width() + 8);
1530     QCOMPARE(input->clipRect().height(), input->height());
1531
1532     input->setHAlign(QQuickTextInput::AlignRight);
1533     QCOMPARE(input->clipRect().x(), qreal(0));
1534     QCOMPARE(input->clipRect().y(), qreal(0));
1535     QCOMPARE(input->clipRect().width(), input->width() + 8);
1536     QCOMPARE(input->clipRect().height(), input->height());
1537
1538     input->setWrapMode(QQuickTextInput::Wrap);
1539     QCOMPARE(input->clipRect().x(), qreal(0));
1540     QCOMPARE(input->clipRect().y(), qreal(0));
1541     QCOMPARE(input->clipRect().width(), input->width() + 8);
1542     QCOMPARE(input->clipRect().height(), input->height());
1543
1544     input->setVAlign(QQuickTextInput::AlignBottom);
1545     QCOMPARE(input->clipRect().x(), qreal(0));
1546     QCOMPARE(input->clipRect().y(), qreal(0));
1547     QCOMPARE(input->clipRect().width(), input->width() + 8);
1548     QCOMPARE(input->clipRect().height(), input->height());
1549 }
1550
1551 void tst_qquicktextinput::boundingRect()
1552 {
1553     QQmlComponent component(&engine);
1554     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1555     QScopedPointer<QObject> object(component.create());
1556     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1557     QVERIFY(input);
1558
1559     QTextLayout layout;
1560     layout.setFont(input->font());
1561
1562     if (!qmlDisableDistanceField()) {
1563         QTextOption option;
1564         option.setUseDesignMetrics(true);
1565         layout.setTextOption(option);
1566     }
1567     layout.beginLayout();
1568     QTextLine line = layout.createLine();
1569     layout.endLayout();
1570
1571     QCOMPARE(input->boundingRect().x(), qreal(0));
1572     QCOMPARE(input->boundingRect().y(), qreal(0));
1573     QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1574     QCOMPARE(input->boundingRect().height(), line.height());
1575
1576     input->setText("Hello World");
1577
1578     layout.setText(input->text());
1579     layout.beginLayout();
1580     line = layout.createLine();
1581     layout.endLayout();
1582
1583     QCOMPARE(input->boundingRect().x(), qreal(0));
1584     QCOMPARE(input->boundingRect().y(), qreal(0));
1585     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1586     QCOMPARE(input->boundingRect().height(), line.height());
1587
1588     // the size of the bounding rect shouldn't be bounded by the size of item.
1589     input->setWidth(input->width() / 2);
1590     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1591     QCOMPARE(input->boundingRect().y(), qreal(0));
1592     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1593     QCOMPARE(input->boundingRect().height(), line.height());
1594
1595     input->setHeight(input->height() * 2);
1596     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1597     QCOMPARE(input->boundingRect().y(), qreal(0));
1598     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1599     QCOMPARE(input->boundingRect().height(), line.height());
1600
1601     QQmlComponent cursorComponent(&engine);
1602     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1603
1604     input->setCursorDelegate(&cursorComponent);
1605     input->setCursorVisible(true);
1606
1607     // Don't include the size of a cursor delegate as it has its own bounding rect.
1608     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1609     QCOMPARE(input->boundingRect().y(), qreal(0));
1610     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1611     QCOMPARE(input->boundingRect().height(), line.height());
1612
1613     // Bounding rect left aligned when auto scroll is disabled;
1614     input->setAutoScroll(false);
1615     QCOMPARE(input->boundingRect().x(), qreal(0));
1616     QCOMPARE(input->boundingRect().y(), qreal(0));
1617     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1618     QCOMPARE(input->boundingRect().height(), line.height());
1619
1620     input->setHAlign(QQuickTextInput::AlignRight);
1621     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1622     QCOMPARE(input->boundingRect().y(), qreal(0));
1623     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1624     QCOMPARE(input->boundingRect().height(), line.height());
1625
1626     input->setWrapMode(QQuickTextInput::Wrap);
1627     QCOMPARE(input->boundingRect().right(), input->width());
1628     QCOMPARE(input->boundingRect().y(), qreal(0));
1629     QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1630     QVERIFY(input->boundingRect().height() > line.height());
1631
1632     input->setVAlign(QQuickTextInput::AlignBottom);
1633     QCOMPARE(input->boundingRect().right(), input->width());
1634     QCOMPARE(input->boundingRect().bottom(), input->height());
1635     QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1636     QVERIFY(input->boundingRect().height() > line.height());
1637 }
1638
1639 void tst_qquicktextinput::positionAt()
1640 {
1641     QQuickView canvas(testFileUrl("positionAt.qml"));
1642     QVERIFY(canvas.rootObject() != 0);
1643     canvas.show();
1644     canvas.requestActivateWindow();
1645     QTest::qWaitForWindowShown(&canvas);
1646
1647     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1648     QVERIFY(textinputObject != 0);
1649
1650     // Check autoscrolled...
1651
1652     int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1653
1654     QTextLayout layout(textinputObject->text());
1655     layout.setFont(textinputObject->font());
1656
1657     if (!qmlDisableDistanceField()) {
1658         QTextOption option;
1659         option.setUseDesignMetrics(true);
1660         layout.setTextOption(option);
1661     }
1662     layout.beginLayout();
1663     QTextLine line = layout.createLine();
1664     layout.endLayout();
1665
1666     int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1667     int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1668     int textWidth = floor(line.horizontalAdvance());
1669
1670     QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1671     QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1672
1673     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1674     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1675     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1676
1677     // Check without autoscroll...
1678     textinputObject->setAutoScroll(false);
1679     pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1680
1681     textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1682     textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1683
1684     QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1685     QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1686
1687     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1688     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1689     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1690
1691     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1692     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1693
1694     QString preeditText = textinputObject->text().mid(0, pos);
1695     textinputObject->setText(textinputObject->text().mid(pos));
1696     textinputObject->setCursorPosition(0);
1697
1698     {   QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1699         QVERIFY(qGuiApp->focusObject());
1700         QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1701
1702     // Check all points within the preedit text return the same position.
1703     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1704     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1705     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1706
1707     // Verify positioning returns to normal after the preedit text.
1708     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1709     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1710
1711     {   QInputMethodEvent inputEvent;
1712         QVERIFY(qGuiApp->focusObject());
1713         QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1714
1715     // With wrapping.
1716     textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1717
1718     const qreal y0 = line.height() / 2;
1719     const qreal y1 = line.height() * 3 / 2;
1720
1721     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1722     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1723
1724     int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1725     QVERIFY(newLinePos > pos);
1726     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1727 }
1728
1729 void tst_qquicktextinput::maxLength()
1730 {
1731     QQuickView canvas(testFileUrl("maxLength.qml"));
1732     QVERIFY(canvas.rootObject() != 0);
1733     canvas.show();
1734     canvas.requestActivateWindow();
1735     QTest::qWaitForWindowShown(&canvas);
1736
1737     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1738     QVERIFY(textinputObject != 0);
1739     QVERIFY(textinputObject->text().isEmpty());
1740     QVERIFY(textinputObject->maxLength() == 10);
1741     foreach (const QString &str, standard) {
1742         QVERIFY(textinputObject->text().length() <= 10);
1743         textinputObject->setText(str);
1744         QVERIFY(textinputObject->text().length() <= 10);
1745     }
1746
1747     textinputObject->setText("");
1748     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1749     for (int i=0; i<20; i++) {
1750         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1751         //simulateKey(&canvas, Qt::Key_A);
1752         QTest::keyPress(&canvas, Qt::Key_A);
1753         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1754         QTest::qWait(50);
1755     }
1756 }
1757
1758 void tst_qquicktextinput::masks()
1759 {
1760     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1761     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1762     QQuickView canvas(testFileUrl("masks.qml"));
1763     canvas.show();
1764     canvas.requestActivateWindow();
1765     QVERIFY(canvas.rootObject() != 0);
1766     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1767     QVERIFY(textinputObject != 0);
1768     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1769     QVERIFY(textinputObject->text().length() == 0);
1770     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1771     QCOMPARE(textinputObject->length(), 8);
1772     for (int i=0; i<10; i++) {
1773         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1774         QCOMPARE(textinputObject->length(), 8);
1775         QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1776         QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1777         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1778         //simulateKey(&canvas, Qt::Key_A);
1779         QTest::keyPress(&canvas, Qt::Key_A);
1780         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1781         QTest::qWait(50);
1782     }
1783 }
1784
1785 void tst_qquicktextinput::validators()
1786 {
1787     // Note that this test assumes that the validators are working properly
1788     // so you may need to run their tests first. All validators are checked
1789     // here to ensure that their exposure to QML is working.
1790
1791     QLocale::setDefault(QLocale(QStringLiteral("C")));
1792
1793     QQuickView canvas(testFileUrl("validators.qml"));
1794     canvas.show();
1795     canvas.requestActivateWindow();
1796
1797     QVERIFY(canvas.rootObject() != 0);
1798
1799     QLocale defaultLocale;
1800     QLocale enLocale("en");
1801     QLocale deLocale("de_DE");
1802
1803     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1804     QVERIFY(intInput);
1805     QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1806
1807     QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1808     QVERIFY(intValidator);
1809     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1810     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1811     intValidator->setLocaleName(enLocale.name());
1812     QCOMPARE(intValidator->localeName(), enLocale.name());
1813     QCOMPARE(intInput->validator()->locale(), enLocale);
1814     intValidator->resetLocaleName();
1815     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1816     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1817
1818     intInput->setFocus(true);
1819     QTRY_VERIFY(intInput->hasActiveFocus());
1820     QCOMPARE(intInput->hasAcceptableInput(), false);
1821     QCOMPARE(intInput->property("acceptable").toBool(), false);
1822     QTest::keyPress(&canvas, Qt::Key_1);
1823     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1824     QTest::qWait(50);
1825     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1826     QCOMPARE(intInput->hasAcceptableInput(), false);
1827     QCOMPARE(intInput->property("acceptable").toBool(), false);
1828     QCOMPARE(intSpy.count(), 0);
1829     QTest::keyPress(&canvas, Qt::Key_2);
1830     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1831     QTest::qWait(50);
1832     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1833     QCOMPARE(intInput->hasAcceptableInput(), false);
1834     QCOMPARE(intInput->property("acceptable").toBool(), false);
1835     QCOMPARE(intSpy.count(), 0);
1836     QTest::keyPress(&canvas, Qt::Key_Period);
1837     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1838     QTest::qWait(50);
1839     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1840     QCOMPARE(intInput->hasAcceptableInput(), false);
1841     QTest::keyPress(&canvas, Qt::Key_Comma);
1842     QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1843     QTest::qWait(50);
1844     QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1845     QCOMPARE(intInput->hasAcceptableInput(), false);
1846     QTest::keyPress(&canvas, Qt::Key_Backspace);
1847     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1848     QTest::qWait(50);
1849     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1850     QCOMPARE(intInput->hasAcceptableInput(), false);
1851     intValidator->setLocaleName(deLocale.name());
1852     QTest::keyPress(&canvas, Qt::Key_Period);
1853     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1854     QTest::qWait(50);
1855     QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1856     QCOMPARE(intInput->hasAcceptableInput(), false);
1857     QTest::keyPress(&canvas, Qt::Key_Backspace);
1858     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1859     QTest::qWait(50);
1860     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1861     QCOMPARE(intInput->hasAcceptableInput(), false);
1862     intValidator->resetLocaleName();
1863     QTest::keyPress(&canvas, Qt::Key_1);
1864     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1865     QTest::qWait(50);
1866     QCOMPARE(intInput->text(), QLatin1String("11"));
1867     QCOMPARE(intInput->hasAcceptableInput(), true);
1868     QCOMPARE(intInput->property("acceptable").toBool(), true);
1869     QCOMPARE(intSpy.count(), 1);
1870     QTest::keyPress(&canvas, Qt::Key_0);
1871     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1872     QTest::qWait(50);
1873     QCOMPARE(intInput->text(), QLatin1String("11"));
1874     QCOMPARE(intInput->hasAcceptableInput(), true);
1875     QCOMPARE(intInput->property("acceptable").toBool(), true);
1876     QCOMPARE(intSpy.count(), 1);
1877
1878     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1879     QVERIFY(dblInput);
1880     QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1881
1882     QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1883     QVERIFY(dblValidator);
1884     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1885     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1886     dblValidator->setLocaleName(enLocale.name());
1887     QCOMPARE(dblValidator->localeName(), enLocale.name());
1888     QCOMPARE(dblInput->validator()->locale(), enLocale);
1889     dblValidator->resetLocaleName();
1890     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1891     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1892
1893     dblInput->setFocus(true);
1894     QVERIFY(dblInput->hasActiveFocus() == true);
1895     QCOMPARE(dblInput->hasAcceptableInput(), false);
1896     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1897     QTest::keyPress(&canvas, Qt::Key_1);
1898     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1899     QTest::qWait(50);
1900     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1901     QCOMPARE(dblInput->hasAcceptableInput(), false);
1902     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1903     QCOMPARE(dblSpy.count(), 0);
1904     QTest::keyPress(&canvas, Qt::Key_2);
1905     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1906     QTest::qWait(50);
1907     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1908     QCOMPARE(dblInput->hasAcceptableInput(), true);
1909     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1910     QCOMPARE(dblSpy.count(), 1);
1911     QTest::keyPress(&canvas, Qt::Key_Comma);
1912     QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1913     QTest::qWait(50);
1914     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1915     QCOMPARE(dblInput->hasAcceptableInput(), true);
1916     QTest::keyPress(&canvas, Qt::Key_1);
1917     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1918     QTest::qWait(50);
1919     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1920     QCOMPARE(dblInput->hasAcceptableInput(), true);
1921     dblValidator->setLocaleName(deLocale.name());
1922     QCOMPARE(dblInput->hasAcceptableInput(), true);
1923     QTest::keyPress(&canvas, Qt::Key_1);
1924     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1925     QTest::qWait(50);
1926     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1927     QCOMPARE(dblInput->hasAcceptableInput(), true);
1928     QTest::keyPress(&canvas, Qt::Key_1);
1929     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1930     QTest::qWait(50);
1931     QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1932     QCOMPARE(dblInput->hasAcceptableInput(), true);
1933     QTest::keyPress(&canvas, Qt::Key_Backspace);
1934     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1935     QTest::qWait(50);
1936     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1937     QCOMPARE(dblInput->hasAcceptableInput(), true);
1938     QTest::keyPress(&canvas, Qt::Key_Backspace);
1939     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1940     QTest::qWait(50);
1941     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1942     QCOMPARE(dblInput->hasAcceptableInput(), true);
1943     QTest::keyPress(&canvas, Qt::Key_Backspace);
1944     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1945     QTest::qWait(50);
1946     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1947     QCOMPARE(dblInput->hasAcceptableInput(), true);
1948     dblValidator->resetLocaleName();
1949     QTest::keyPress(&canvas, Qt::Key_Period);
1950     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1951     QTest::qWait(50);
1952     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1953     QCOMPARE(dblInput->hasAcceptableInput(), true);
1954     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1955     QCOMPARE(dblSpy.count(), 1);
1956     QTest::keyPress(&canvas, Qt::Key_1);
1957     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1958     QTest::qWait(50);
1959     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1960     QCOMPARE(dblInput->hasAcceptableInput(), true);
1961     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1962     QCOMPARE(dblSpy.count(), 1);
1963     QTest::keyPress(&canvas, Qt::Key_1);
1964     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1965     QTest::qWait(50);
1966     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1967     QCOMPARE(dblInput->hasAcceptableInput(), true);
1968     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1969     QCOMPARE(dblSpy.count(), 1);
1970     QTest::keyPress(&canvas, Qt::Key_1);
1971     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1972     QTest::qWait(50);
1973     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1974     QCOMPARE(dblInput->hasAcceptableInput(), true);
1975     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1976     QCOMPARE(dblSpy.count(), 1);
1977
1978     // Ensure the validator doesn't prevent characters being removed.
1979     dblInput->setValidator(intInput->validator());
1980     QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1981     QCOMPARE(dblInput->hasAcceptableInput(), false);
1982     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1983     QCOMPARE(dblSpy.count(), 2);
1984     QTest::keyPress(&canvas, Qt::Key_Backspace);
1985     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1986     QTest::qWait(50);
1987     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1988     QCOMPARE(dblInput->hasAcceptableInput(), false);
1989     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1990     QCOMPARE(dblSpy.count(), 2);
1991     // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1992     QTest::keyPress(&canvas, Qt::Key_1);
1993     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1994     QTest::qWait(50);
1995     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1996     QCOMPARE(dblInput->hasAcceptableInput(), false);
1997     QCOMPARE(dblSpy.count(), 2);
1998     QTest::keyPress(&canvas, Qt::Key_Backspace);
1999     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2000     QTest::qWait(50);
2001     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
2002     QCOMPARE(dblInput->hasAcceptableInput(), false);
2003     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2004     QCOMPARE(dblSpy.count(), 2);
2005     QTest::keyPress(&canvas, Qt::Key_Backspace);
2006     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2007     QTest::qWait(50);
2008     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2009     QCOMPARE(dblInput->hasAcceptableInput(), false);
2010     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2011     QCOMPARE(dblSpy.count(), 2);
2012     QTest::keyPress(&canvas, Qt::Key_Backspace);
2013     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2014     QTest::qWait(50);
2015     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2016     QCOMPARE(dblInput->hasAcceptableInput(), false);
2017     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2018     QCOMPARE(dblSpy.count(), 2);
2019     QTest::keyPress(&canvas, Qt::Key_Backspace);
2020     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2021     QTest::qWait(50);
2022     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2023     QCOMPARE(dblInput->hasAcceptableInput(), false);
2024     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2025     QCOMPARE(dblSpy.count(), 2);
2026     QTest::keyPress(&canvas, Qt::Key_1);
2027     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2028     QTest::qWait(50);
2029     QCOMPARE(dblInput->text(), QLatin1String("11"));
2030     QCOMPARE(dblInput->property("acceptable").toBool(), true);
2031     QCOMPARE(dblInput->hasAcceptableInput(), true);
2032     QCOMPARE(dblSpy.count(), 3);
2033
2034     // Changing the validator properties will re-evaluate whether the input is acceptable.
2035     intValidator->setTop(10);
2036     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2037     QCOMPARE(dblInput->hasAcceptableInput(), false);
2038     QCOMPARE(dblSpy.count(), 4);
2039     intValidator->setTop(12);
2040     QCOMPARE(dblInput->property("acceptable").toBool(), true);
2041     QCOMPARE(dblInput->hasAcceptableInput(), true);
2042     QCOMPARE(dblSpy.count(), 5);
2043
2044     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
2045     QVERIFY(strInput);
2046     QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2047     strInput->setFocus(true);
2048     QVERIFY(strInput->hasActiveFocus() == true);
2049     QCOMPARE(strInput->hasAcceptableInput(), false);
2050     QCOMPARE(strInput->property("acceptable").toBool(), false);
2051     QTest::keyPress(&canvas, Qt::Key_1);
2052     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2053     QTest::qWait(50);
2054     QTRY_COMPARE(strInput->text(), QLatin1String(""));
2055     QCOMPARE(strInput->hasAcceptableInput(), false);
2056     QCOMPARE(strInput->property("acceptable").toBool(), false);
2057     QCOMPARE(strSpy.count(), 0);
2058     QTest::keyPress(&canvas, Qt::Key_A);
2059     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2060     QTest::qWait(50);
2061     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2062     QCOMPARE(strInput->hasAcceptableInput(), false);
2063     QCOMPARE(strInput->property("acceptable").toBool(), false);
2064     QCOMPARE(strSpy.count(), 0);
2065     QTest::keyPress(&canvas, Qt::Key_A);
2066     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2067     QTest::qWait(50);
2068     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2069     QCOMPARE(strInput->hasAcceptableInput(), true);
2070     QCOMPARE(strInput->property("acceptable").toBool(), true);
2071     QCOMPARE(strSpy.count(), 1);
2072     QTest::keyPress(&canvas, Qt::Key_A);
2073     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2074     QTest::qWait(50);
2075     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2076     QCOMPARE(strInput->hasAcceptableInput(), true);
2077     QCOMPARE(strInput->property("acceptable").toBool(), true);
2078     QCOMPARE(strSpy.count(), 1);
2079     QTest::keyPress(&canvas, Qt::Key_A);
2080     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2081     QTest::qWait(50);
2082     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2083     QCOMPARE(strInput->hasAcceptableInput(), true);
2084     QCOMPARE(strInput->property("acceptable").toBool(), true);
2085     QCOMPARE(strSpy.count(), 1);
2086     QTest::keyPress(&canvas, Qt::Key_A);
2087     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2088     QTest::qWait(50);
2089     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2090     QCOMPARE(strInput->hasAcceptableInput(), true);
2091     QCOMPARE(strInput->property("acceptable").toBool(), true);
2092     QCOMPARE(strSpy.count(), 1);
2093
2094     QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
2095     QVERIFY(unvalidatedInput);
2096     QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2097     unvalidatedInput->setFocus(true);
2098     QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2099     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2100     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2101     QTest::keyPress(&canvas, Qt::Key_1);
2102     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2103     QTest::qWait(50);
2104     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2105     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2106     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2107     QCOMPARE(unvalidatedSpy.count(), 0);
2108     QTest::keyPress(&canvas, Qt::Key_A);
2109     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2110     QTest::qWait(50);
2111     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2112     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2113     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2114     QCOMPARE(unvalidatedSpy.count(), 0);
2115 }
2116
2117 void tst_qquicktextinput::inputMethods()
2118 {
2119     QQuickView canvas(testFileUrl("inputmethods.qml"));
2120     canvas.show();
2121     canvas.requestActivateWindow();
2122     QTest::qWaitForWindowShown(&canvas);
2123
2124     // test input method hints
2125     QVERIFY(canvas.rootObject() != 0);
2126     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
2127     QVERIFY(input != 0);
2128     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2129     QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2130     input->setInputMethodHints(Qt::ImhUppercaseOnly);
2131     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2132     QCOMPARE(inputMethodHintSpy.count(), 1);
2133     input->setInputMethodHints(Qt::ImhUppercaseOnly);
2134     QCOMPARE(inputMethodHintSpy.count(), 1);
2135
2136     // default value
2137     QQuickTextInput plainInput;
2138     QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2139
2140     input->setFocus(true);
2141     QVERIFY(input->hasActiveFocus() == true);
2142     // test that input method event is committed
2143     QInputMethodEvent event;
2144     event.setCommitString( "My ", -12, 0);
2145     QTRY_COMPARE(qGuiApp->focusObject(), input);
2146     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2147     QCOMPARE(input->text(), QString("My Hello world!"));
2148
2149     input->setCursorPosition(2);
2150     event.setCommitString("Your", -2, 2);
2151     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2152     QCOMPARE(input->text(), QString("Your Hello world!"));
2153     QCOMPARE(input->cursorPosition(), 4);
2154
2155     input->setCursorPosition(7);
2156     event.setCommitString("Goodbye", -2, 5);
2157     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2158     QCOMPARE(input->text(), QString("Your Goodbye world!"));
2159     QCOMPARE(input->cursorPosition(), 12);
2160
2161     input->setCursorPosition(8);
2162     event.setCommitString("Our", -8, 4);
2163     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2164     QCOMPARE(input->text(), QString("Our Goodbye world!"));
2165     QCOMPARE(input->cursorPosition(), 7);
2166
2167     // input should reset selection even if replacement parameters are out of bounds
2168     input->setText("text");
2169     input->setCursorPosition(0);
2170     input->moveCursorSelection(input->text().length());
2171     event.setCommitString("replacement", -input->text().length(), input->text().length());
2172     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2173     QCOMPARE(input->selectionStart(), input->selectionEnd());
2174
2175     QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2176     QGuiApplication::sendEvent(input, &enabledQueryEvent);
2177     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2178
2179     input->setReadOnly(true);
2180     QGuiApplication::sendEvent(input, &enabledQueryEvent);
2181     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2182 }
2183
2184 /*
2185 TextInput element should only handle left/right keys until the cursor reaches
2186 the extent of the text, then they should ignore the keys.
2187
2188 */
2189 void tst_qquicktextinput::navigation()
2190 {
2191     QQuickView canvas(testFileUrl("navigation.qml"));
2192     canvas.show();
2193     canvas.requestActivateWindow();
2194
2195     QVERIFY(canvas.rootObject() != 0);
2196
2197     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2198
2199     QVERIFY(input != 0);
2200     input->setCursorPosition(0);
2201     QTRY_VERIFY(input->hasActiveFocus() == true);
2202     simulateKey(&canvas, Qt::Key_Left);
2203     QVERIFY(input->hasActiveFocus() == false);
2204     simulateKey(&canvas, Qt::Key_Right);
2205     QVERIFY(input->hasActiveFocus() == true);
2206     //QT-2944: If text is selected, ensure we deselect upon cursor motion
2207     input->setCursorPosition(input->text().length());
2208     input->select(0,input->text().length());
2209     QVERIFY(input->selectionStart() != input->selectionEnd());
2210     simulateKey(&canvas, Qt::Key_Right);
2211     QVERIFY(input->selectionStart() == input->selectionEnd());
2212     QVERIFY(input->selectionStart() == input->text().length());
2213     QVERIFY(input->hasActiveFocus() == true);
2214     simulateKey(&canvas, Qt::Key_Right);
2215     QVERIFY(input->hasActiveFocus() == false);
2216     simulateKey(&canvas, Qt::Key_Left);
2217     QVERIFY(input->hasActiveFocus() == true);
2218
2219     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2220     input->setCursorPosition(2);
2221     QCOMPARE(input->cursorPosition(),2);
2222     simulateKey(&canvas, Qt::Key_Up);
2223     QCOMPARE(input->cursorPosition(),2);
2224     simulateKey(&canvas, Qt::Key_Down);
2225     QCOMPARE(input->cursorPosition(),2);
2226
2227     // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2228     input->setText(QString());
2229     QCOMPARE(input->cursorPosition(), 0);
2230     simulateKey(&canvas, Qt::Key_Right);
2231     QCOMPARE(input->hasActiveFocus(), false);
2232     simulateKey(&canvas, Qt::Key_Left);
2233     QCOMPARE(input->hasActiveFocus(), true);
2234     simulateKey(&canvas, Qt::Key_Left);
2235     QCOMPARE(input->hasActiveFocus(), false);
2236 }
2237
2238 void tst_qquicktextinput::navigation_RTL()
2239 {
2240     QQuickView canvas(testFileUrl("navigation.qml"));
2241     canvas.show();
2242     canvas.requestActivateWindow();
2243
2244     QVERIFY(canvas.rootObject() != 0);
2245
2246     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2247
2248     QVERIFY(input != 0);
2249     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2250     input->setText(QString::fromUtf16(arabic_str, 11));
2251
2252     input->setCursorPosition(0);
2253     QTRY_VERIFY(input->hasActiveFocus() == true);
2254
2255     // move off
2256     simulateKey(&canvas, Qt::Key_Right);
2257     QVERIFY(input->hasActiveFocus() == false);
2258
2259     // move back
2260     simulateKey(&canvas, Qt::Key_Left);
2261     QVERIFY(input->hasActiveFocus() == true);
2262
2263     input->setCursorPosition(input->text().length());
2264     QVERIFY(input->hasActiveFocus() == true);
2265
2266     // move off
2267     simulateKey(&canvas, Qt::Key_Left);
2268     QVERIFY(input->hasActiveFocus() == false);
2269
2270     // move back
2271     simulateKey(&canvas, Qt::Key_Right);
2272     QVERIFY(input->hasActiveFocus() == true);
2273 }
2274
2275 void tst_qquicktextinput::copyAndPaste() {
2276 #ifndef QT_NO_CLIPBOARD
2277
2278 #ifdef Q_OS_MAC
2279     {
2280         PasteboardRef pasteboard;
2281         OSStatus status = PasteboardCreate(0, &pasteboard);
2282         if (status == noErr)
2283             CFRelease(pasteboard);
2284         else
2285             QSKIP("This machine doesn't support the clipboard");
2286     }
2287 #endif
2288
2289     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2290     QQmlComponent textInputComponent(&engine);
2291     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2292     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2293     QVERIFY(textInput != 0);
2294
2295     // copy and paste
2296     QCOMPARE(textInput->text().length(), 12);
2297     textInput->select(0, textInput->text().length());
2298     textInput->copy();
2299     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2300     QCOMPARE(textInput->selectedText().length(), 12);
2301     textInput->setCursorPosition(0);
2302     QVERIFY(textInput->canPaste());
2303     textInput->paste();
2304     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2305     QCOMPARE(textInput->text().length(), 24);
2306
2307     // can paste
2308     QVERIFY(textInput->canPaste());
2309     textInput->setReadOnly(true);
2310     QVERIFY(!textInput->canPaste());
2311     textInput->setReadOnly(false);
2312     QVERIFY(textInput->canPaste());
2313
2314     // select word
2315     textInput->setCursorPosition(0);
2316     textInput->selectWord();
2317     QCOMPARE(textInput->selectedText(), QString("Hello"));
2318
2319     // select all and cut
2320     textInput->selectAll();
2321     textInput->cut();
2322     QCOMPARE(textInput->text().length(), 0);
2323     textInput->paste();
2324     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2325     QCOMPARE(textInput->text().length(), 24);
2326
2327     // clear copy buffer
2328     QClipboard *clipboard = QGuiApplication::clipboard();
2329     QVERIFY(clipboard);
2330     clipboard->clear();
2331     QVERIFY(!textInput->canPaste());
2332
2333     // test that copy functionality is disabled
2334     // when echo mode is set to hide text/password mode
2335     int index = 0;
2336     while (index < 4) {
2337         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2338         textInput->setEchoMode(echoMode);
2339         textInput->setText("My password");
2340         textInput->select(0, textInput->text().length());
2341         textInput->copy();
2342         if (echoMode == QQuickTextInput::Normal) {
2343             QVERIFY(!clipboard->text().isEmpty());
2344             QCOMPARE(clipboard->text(), QString("My password"));
2345             clipboard->clear();
2346         } else {
2347             QVERIFY(clipboard->text().isEmpty());
2348         }
2349         index++;
2350     }
2351
2352     delete textInput;
2353 #endif
2354 }
2355
2356 void tst_qquicktextinput::copyAndPasteKeySequence() {
2357 #ifndef QT_NO_CLIPBOARD
2358
2359 #ifdef Q_OS_MAC
2360     {
2361         PasteboardRef pasteboard;
2362         OSStatus status = PasteboardCreate(0, &pasteboard);
2363         if (status == noErr)
2364             CFRelease(pasteboard);
2365         else
2366             QSKIP("This machine doesn't support the clipboard");
2367     }
2368 #endif
2369
2370     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2371     QQmlComponent textInputComponent(&engine);
2372     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2373     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2374     QVERIFY(textInput != 0);
2375
2376     QQuickCanvas canvas;
2377     textInput->setParentItem(canvas.rootItem());
2378     canvas.show();
2379     canvas.requestActivateWindow();
2380     QTest::qWaitForWindowShown(&canvas);
2381     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
2382
2383     // copy and paste
2384     QVERIFY(textInput->hasActiveFocus());
2385     QCOMPARE(textInput->text().length(), 12);
2386     textInput->select(0, textInput->text().length());
2387     simulateKeys(&canvas, QKeySequence::Copy);
2388     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2389     QCOMPARE(textInput->selectedText().length(), 12);
2390     textInput->setCursorPosition(0);
2391     QVERIFY(textInput->canPaste());
2392     simulateKeys(&canvas, QKeySequence::Paste);
2393     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2394     QCOMPARE(textInput->text().length(), 24);
2395
2396     // select all and cut
2397     simulateKeys(&canvas, QKeySequence::SelectAll);
2398     simulateKeys(&canvas, QKeySequence::Cut);
2399     QCOMPARE(textInput->text().length(), 0);
2400     simulateKeys(&canvas, QKeySequence::Paste);
2401     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2402     QCOMPARE(textInput->text().length(), 24);
2403
2404     // clear copy buffer
2405     QClipboard *clipboard = QGuiApplication::clipboard();
2406     QVERIFY(clipboard);
2407     clipboard->clear();
2408     QVERIFY(!textInput->canPaste());
2409
2410     // test that copy functionality is disabled
2411     // when echo mode is set to hide text/password mode
2412     int index = 0;
2413     while (index < 4) {
2414         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2415         textInput->setEchoMode(echoMode);
2416         textInput->setText("My password");
2417         textInput->select(0, textInput->text().length());
2418         simulateKeys(&canvas, QKeySequence::Copy);
2419         if (echoMode == QQuickTextInput::Normal) {
2420             QVERIFY(!clipboard->text().isEmpty());
2421             QCOMPARE(clipboard->text(), QString("My password"));
2422             clipboard->clear();
2423         } else {
2424             QVERIFY(clipboard->text().isEmpty());
2425         }
2426         index++;
2427     }
2428
2429     delete textInput;
2430 #endif
2431 }
2432
2433 void tst_qquicktextinput::canPasteEmpty() {
2434 #ifndef QT_NO_CLIPBOARD
2435
2436     QGuiApplication::clipboard()->clear();
2437
2438     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2439     QQmlComponent textInputComponent(&engine);
2440     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2441     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2442     QVERIFY(textInput != 0);
2443
2444     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2445     QCOMPARE(textInput->canPaste(), cp);
2446
2447 #endif
2448 }
2449
2450 void tst_qquicktextinput::canPaste() {
2451 #ifndef QT_NO_CLIPBOARD
2452
2453     QGuiApplication::clipboard()->setText("Some text");
2454
2455     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2456     QQmlComponent textInputComponent(&engine);
2457     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2458     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2459     QVERIFY(textInput != 0);
2460
2461     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2462     QCOMPARE(textInput->canPaste(), cp);
2463
2464 #endif
2465 }
2466
2467 void tst_qquicktextinput::passwordCharacter()
2468 {
2469     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2470     QQmlComponent textInputComponent(&engine);
2471     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2472     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2473     QVERIFY(textInput != 0);
2474
2475     textInput->setPasswordCharacter("X");
2476     qreal implicitWidth = textInput->implicitWidth();
2477     textInput->setPasswordCharacter(".");
2478
2479     // QTBUG-12383 content is updated and redrawn
2480     QVERIFY(textInput->implicitWidth() < implicitWidth);
2481
2482     delete textInput;
2483 }
2484
2485 void tst_qquicktextinput::cursorDelegate_data()
2486 {
2487     QTest::addColumn<QUrl>("source");
2488     QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2489     QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2490     QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2491 }
2492
2493 void tst_qquicktextinput::cursorDelegate()
2494 {
2495     QFETCH(QUrl, source);
2496     QQuickView view(source);
2497     view.show();
2498     view.requestActivateWindow();
2499     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2500     QVERIFY(textInputObject != 0);
2501     // Delegate is created on demand, and so won't be available immediately.  Focus in or
2502     // setCursorVisible(true) will trigger creation.
2503     QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2504     QVERIFY(!textInputObject->isCursorVisible());
2505     //Test Delegate gets created
2506     textInputObject->setFocus(true);
2507     QVERIFY(textInputObject->isCursorVisible());
2508     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2509     QVERIFY(delegateObject);
2510     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2511     //Test Delegate gets moved
2512     for (int i=0; i<= textInputObject->text().length(); i++) {
2513         textInputObject->setCursorPosition(i);
2514         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2515         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2516     }
2517     textInputObject->setCursorPosition(0);
2518     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2519     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2520
2521     // Test delegate gets moved on mouse press.
2522     textInputObject->setSelectByMouse(true);
2523     textInputObject->setCursorPosition(0);
2524     const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2525     QTest::qWait(400);  //ensure this isn't treated as a double-click
2526     QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2527     QTest::qWait(50);
2528     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2529     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2530     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2531
2532     // Test delegate gets moved on mouse drag
2533     textInputObject->setCursorPosition(0);
2534     const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2535     QTest::qWait(400);  //ensure this isn't treated as a double-click
2536     QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2537     QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2538     QGuiApplication::sendEvent(&view, &mv);
2539     QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2540     QTest::qWait(50);
2541     QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2542     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2543
2544     textInputObject->setReadOnly(true);
2545     textInputObject->setCursorPosition(0);
2546     QTest::qWait(400);  //ensure this isn't treated as a double-click
2547     QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2548     QTest::qWait(50);
2549     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2550     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2551     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2552
2553     textInputObject->setCursorPosition(0);
2554     QTest::qWait(400);  //ensure this isn't treated as a double-click
2555     QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2556     QTest::qWait(50);
2557     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2558     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2559     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2560
2561     textInputObject->setCursorPosition(0);
2562     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2563     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2564
2565     textInputObject->setReadOnly(false);
2566
2567     // Delegate moved when text is entered
2568     textInputObject->setText(QString());
2569     for (int i = 0; i < 20; ++i) {
2570         QTest::keyClick(&view, Qt::Key_A);
2571         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2572         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2573     }
2574
2575     // Delegate moved when text is entered by im.
2576     textInputObject->setText(QString());
2577     for (int i = 0; i < 20; ++i) {
2578         QInputMethodEvent event;
2579         event.setCommitString("w");
2580         QGuiApplication::sendEvent(textInputObject, &event);
2581         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2582         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2583     }
2584     // Delegate moved when text is removed by im.
2585     for (int i = 19; i > 1; --i) {
2586         QInputMethodEvent event;
2587         event.setCommitString(QString(), -1, 1);
2588         QGuiApplication::sendEvent(textInputObject, &event);
2589         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2590         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2591     }
2592     {   // Delegate moved the text is changed in place by im.
2593         QInputMethodEvent event;
2594         event.setCommitString("i", -1, 1);
2595         QGuiApplication::sendEvent(textInputObject, &event);
2596         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2597         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2598     }
2599
2600     //Test Delegate gets deleted
2601     textInputObject->setCursorDelegate(0);
2602     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2603 }
2604
2605 void tst_qquicktextinput::cursorVisible()
2606 {
2607     QQuickTextInput input;
2608     input.componentComplete();
2609     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2610
2611     QQuickView view(testFileUrl("cursorVisible.qml"));
2612     view.show();
2613     view.requestActivateWindow();
2614     QTest::qWaitForWindowShown(&view);
2615     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2616
2617     QCOMPARE(input.isCursorVisible(), false);
2618
2619     input.setCursorVisible(true);
2620     QCOMPARE(input.isCursorVisible(), true);
2621     QCOMPARE(spy.count(), 1);
2622
2623     input.setCursorVisible(false);
2624     QCOMPARE(input.isCursorVisible(), false);
2625     QCOMPARE(spy.count(), 2);
2626
2627     input.setFocus(true);
2628     QCOMPARE(input.isCursorVisible(), false);
2629     QCOMPARE(spy.count(), 2);
2630
2631     input.setParentItem(view.rootObject());
2632     QCOMPARE(input.isCursorVisible(), true);
2633     QCOMPARE(spy.count(), 3);
2634
2635     input.setFocus(false);
2636     QCOMPARE(input.isCursorVisible(), false);
2637     QCOMPARE(spy.count(), 4);
2638
2639     input.setFocus(true);
2640     QCOMPARE(input.isCursorVisible(), true);
2641     QCOMPARE(spy.count(), 5);
2642
2643     QWindow alternateView;
2644     alternateView.show();
2645     alternateView.requestActivateWindow();
2646     QTest::qWaitForWindowShown(&alternateView);
2647
2648     QCOMPARE(input.isCursorVisible(), false);
2649     QCOMPARE(spy.count(), 6);
2650
2651     view.requestActivateWindow();
2652     QTest::qWaitForWindowShown(&view);
2653     QCOMPARE(input.isCursorVisible(), true);
2654     QCOMPARE(spy.count(), 7);
2655
2656     {   // Cursor attribute with 0 length hides cursor.
2657         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2658                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2659         QCoreApplication::sendEvent(&input, &ev);
2660     }
2661     QCOMPARE(input.isCursorVisible(), false);
2662     QCOMPARE(spy.count(), 8);
2663
2664     {   // Cursor attribute with non zero length shows cursor.
2665         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2666                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2667         QCoreApplication::sendEvent(&input, &ev);
2668     }
2669     QCOMPARE(input.isCursorVisible(), true);
2670     QCOMPARE(spy.count(), 9);
2671
2672     {   // If the cursor is hidden by the input method and the text is changed it should be visible again.
2673         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2674                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2675         QCoreApplication::sendEvent(&input, &ev);
2676     }
2677     QCOMPARE(input.isCursorVisible(), false);
2678     QCOMPARE(spy.count(), 10);
2679
2680     input.setText("something");
2681     QCOMPARE(input.isCursorVisible(), true);
2682     QCOMPARE(spy.count(), 11);
2683
2684     {   // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2685         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2686                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2687         QCoreApplication::sendEvent(&input, &ev);
2688     }
2689     QCOMPARE(input.isCursorVisible(), false);
2690     QCOMPARE(spy.count(), 12);
2691
2692     input.setCursorPosition(5);
2693     QCOMPARE(input.isCursorVisible(), true);
2694     QCOMPARE(spy.count(), 13);
2695 }
2696
2697 void tst_qquicktextinput::cursorRectangle_data()
2698 {
2699     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2700
2701     QTest::addColumn<QString>("text");
2702     QTest::addColumn<int>("positionAtWidth");
2703     QTest::addColumn<int>("wrapPosition");
2704     QTest::addColumn<QString>("shortText");
2705     QTest::addColumn<bool>("leftToRight");
2706
2707     QTest::newRow("left to right")
2708             << "Hello      World!" << 5 << 11
2709             << "Hi"
2710             << true;
2711     QTest::newRow("right to left")
2712             << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2713             << QString::fromUtf16(arabic_str, 3)
2714             << false;
2715 }
2716
2717 void tst_qquicktextinput::cursorRectangle()
2718 {
2719     QFETCH(QString, text);
2720     QFETCH(int, positionAtWidth);
2721     QFETCH(int, wrapPosition);
2722     QFETCH(QString, shortText);
2723     QFETCH(bool, leftToRight);
2724
2725     QQuickTextInput input;
2726     input.setText(text);
2727     input.componentComplete();
2728
2729     QTextLayout layout(text);
2730     layout.setFont(input.font());
2731     if (!qmlDisableDistanceField()) {
2732         QTextOption option;
2733         option.setUseDesignMetrics(true);
2734         layout.setTextOption(option);
2735     }
2736     layout.beginLayout();
2737     QTextLine line = layout.createLine();
2738     layout.endLayout();
2739
2740     qreal offset = 0;
2741     if (leftToRight) {
2742         input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2743     } else {
2744         input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2745         offset = line.horizontalAdvance() - input.width();
2746     }
2747     input.setHeight(qCeil(line.height() * 3 / 2));
2748
2749     QRectF r;
2750
2751     for (int i = 0; i <= positionAtWidth; ++i) {
2752         input.setCursorPosition(i);
2753         r = input.cursorRectangle();
2754
2755         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2756         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2757         QCOMPARE(input.positionToRectangle(i), r);
2758     }
2759
2760     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2761     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2762
2763     for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2764         input.setCursorPosition(i);
2765         QCOMPARE(r, input.cursorRectangle());
2766         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2767         QCOMPARE(input.positionToRectangle(i), r);
2768     }
2769
2770     for (int i = text.length() - 2; i >= 0; --i) {
2771         input.setCursorPosition(i);
2772         r = input.cursorRectangle();
2773         QCOMPARE(r.top(), 0.);
2774         if (leftToRight) {
2775             QVERIFY(r.right() >= 0);
2776         } else {
2777             QVERIFY(r.left() <= input.width());
2778         }
2779         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2780         QCOMPARE(input.positionToRectangle(i), r);
2781     }
2782
2783     // Check position with word wrap.
2784     input.setWrapMode(QQuickTextInput::WordWrap);
2785     input.setAutoScroll(false);
2786     for (int i = 0; i < wrapPosition; ++i) {
2787         input.setCursorPosition(i);
2788         r = input.cursorRectangle();
2789
2790         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2791         QCOMPARE(r.top(), 0.);
2792         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2793         QCOMPARE(input.positionToRectangle(i), r);
2794     }
2795
2796     input.setCursorPosition(wrapPosition);
2797     r = input.cursorRectangle();
2798     if (leftToRight) {
2799         QCOMPARE(r.left(), 0.);
2800     } else {
2801         QCOMPARE(r.left(), input.width());
2802     }
2803     QVERIFY(r.top() >= line.height() - 1);
2804     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2805     QCOMPARE(input.positionToRectangle(11), r);
2806
2807     for (int i = wrapPosition + 1; i < text.length(); ++i) {
2808         input.setCursorPosition(i);
2809         r = input.cursorRectangle();
2810         QVERIFY(r.top() >= line.height() - 1);
2811         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2812         QCOMPARE(input.positionToRectangle(i), r);
2813     }
2814
2815     // Check vertical scrolling with word wrap.
2816     input.setAutoScroll(true);
2817     for (int i = 0; i <= positionAtWidth; ++i) {
2818         input.setCursorPosition(i);
2819         r = input.cursorRectangle();
2820
2821         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2822         QCOMPARE(r.top(), 0.);
2823         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2824         QCOMPARE(input.positionToRectangle(i), r);
2825     }
2826
2827     // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
2828     // reaches the next non-whitespace character.
2829     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2830     for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
2831         input.setCursorPosition(i);
2832         QCOMPARE(r, input.cursorRectangle());
2833         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2834         QCOMPARE(input.positionToRectangle(i), r);
2835     }
2836
2837     input.setCursorPosition(wrapPosition);
2838     r = input.cursorRectangle();
2839     if (leftToRight) {
2840         QCOMPARE(r.left(), 0.);
2841     } else {
2842         QCOMPARE(r.left(), input.width());
2843     }
2844     QVERIFY(r.bottom() >= input.height());
2845     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2846     QCOMPARE(input.positionToRectangle(11), r);
2847
2848     for (int i = wrapPosition + 1; i < text.length(); ++i) {
2849         input.setCursorPosition(i);
2850         r = input.cursorRectangle();
2851         QVERIFY(r.bottom() >= input.height());
2852         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2853         QCOMPARE(input.positionToRectangle(i), r);
2854     }
2855
2856     for (int i = text.length() - 2; i >= wrapPosition; --i) {
2857         input.setCursorPosition(i);
2858         r = input.cursorRectangle();
2859         QVERIFY(r.bottom() >= input.height());
2860         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2861         QCOMPARE(input.positionToRectangle(i), r);
2862     }
2863
2864     input.setCursorPosition(wrapPosition - 1);
2865     r = input.cursorRectangle();
2866     QCOMPARE(r.top(), 0.);
2867     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2868     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2869     QCOMPARE(input.positionToRectangle(10), r);
2870
2871     for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
2872         input.setCursorPosition(i);
2873         QCOMPARE(r, input.cursorRectangle());
2874         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2875         QCOMPARE(input.positionToRectangle(i), r);
2876     }
2877
2878     for (int i = positionAtWidth; i >= 0; --i) {
2879         input.setCursorPosition(i);
2880         r = input.cursorRectangle();
2881         QCOMPARE(r.top(), 0.);
2882         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2883         QCOMPARE(input.positionToRectangle(i), r);
2884     }
2885
2886     input.setText(shortText);
2887     input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
2888     r = input.cursorRectangle();
2889     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2890 }
2891
2892 void tst_qquicktextinput::readOnly()
2893 {
2894     QQuickView canvas(testFileUrl("readOnly.qml"));
2895     canvas.show();
2896     canvas.requestActivateWindow();
2897
2898     QVERIFY(canvas.rootObject() != 0);
2899
2900     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2901
2902     QVERIFY(input != 0);
2903     QTRY_VERIFY(input->hasActiveFocus() == true);
2904     QVERIFY(input->isReadOnly() == true);
2905     QString initial = input->text();
2906     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2907         simulateKey(&canvas, k);
2908     simulateKey(&canvas, Qt::Key_Return);
2909     simulateKey(&canvas, Qt::Key_Space);
2910     simulateKey(&canvas, Qt::Key_Escape);
2911     QCOMPARE(input->text(), initial);
2912
2913     input->setCursorPosition(3);
2914     input->setReadOnly(false);
2915     QCOMPARE(input->isReadOnly(), false);
2916     QCOMPARE(input->cursorPosition(), input->text().length());
2917 }
2918
2919 void tst_qquicktextinput::echoMode()
2920 {
2921     QQuickView canvas(testFileUrl("echoMode.qml"));
2922     canvas.show();
2923     canvas.requestActivateWindow();
2924     QTest::qWaitForWindowShown(&canvas);
2925     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2926
2927     QVERIFY(canvas.rootObject() != 0);
2928
2929     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2930
2931     QVERIFY(input != 0);
2932     QTRY_VERIFY(input->hasActiveFocus() == true);
2933     QString initial = input->text();
2934     Qt::InputMethodHints ref;
2935     QCOMPARE(initial, QLatin1String("ABCDefgh"));
2936     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2937     QCOMPARE(input->displayText(), input->text());
2938     //Normal
2939     ref &= ~Qt::ImhHiddenText;
2940     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2941     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2942     input->setEchoMode(QQuickTextInput::NoEcho);
2943     QCOMPARE(input->text(), initial);
2944     QCOMPARE(input->displayText(), QLatin1String(""));
2945     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2946     //NoEcho
2947     ref |= Qt::ImhHiddenText;
2948     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2949     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2950     input->setEchoMode(QQuickTextInput::Password);
2951     //Password
2952     ref |= Qt::ImhHiddenText;
2953     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2954     QCOMPARE(input->text(), initial);
2955     QCOMPARE(input->displayText(), QLatin1String("********"));
2956     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2957     // clearing input hints do not clear bits set by echo mode
2958     input->setInputMethodHints(Qt::ImhNone);
2959     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2960     input->setPasswordCharacter(QChar('Q'));
2961     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2962     QCOMPARE(input->text(), initial);
2963     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2964     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2965     //PasswordEchoOnEdit
2966     ref &= ~Qt::ImhHiddenText;
2967     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2968     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2969     QCOMPARE(input->text(), initial);
2970     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2971     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2972     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2973     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2974     QCOMPARE(input->text(), QLatin1String("a"));
2975     QCOMPARE(input->displayText(), QLatin1String("a"));
2976     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2977     input->setFocus(false);
2978     QVERIFY(input->hasActiveFocus() == false);
2979     QCOMPARE(input->displayText(), QLatin1String("Q"));
2980     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2981     input->setFocus(true);
2982     QVERIFY(input->hasActiveFocus());
2983     QInputMethodEvent inputEvent;
2984     inputEvent.setCommitString(initial);
2985     QGuiApplication::sendEvent(input, &inputEvent);
2986     QCOMPARE(input->text(), initial);
2987     QCOMPARE(input->displayText(), initial);
2988     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2989 }
2990
2991 void tst_qquicktextinput::passwordEchoDelay()
2992 {
2993     int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
2994     if (maskDelay <= 0)
2995         QSKIP("No mask delay in use");
2996     QQuickView canvas(testFileUrl("echoMode.qml"));
2997     canvas.show();
2998     canvas.requestActivateWindow();
2999     QTest::qWaitForWindowShown(&canvas);
3000     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
3001
3002     QVERIFY(canvas.rootObject() != 0);
3003
3004     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
3005     QVERIFY(input);
3006
3007     QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3008     QVERIFY(cursor);
3009
3010     QChar fillChar = QLatin1Char('*');
3011
3012     input->setEchoMode(QQuickTextInput::Password);
3013     QCOMPARE(input->displayText(), QString(8, fillChar));
3014     input->setText(QString());
3015     QCOMPARE(input->displayText(), QString());
3016
3017     QTest::keyPress(&canvas, '0');
3018     QTest::keyPress(&canvas, '1');
3019     QTest::keyPress(&canvas, '2');
3020     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
3021     QTest::keyPress(&canvas, '3');
3022     QTest::keyPress(&canvas, '4');
3023     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3024     QTest::keyPress(&canvas, Qt::Key_Backspace);
3025     QCOMPARE(input->displayText(), QString(4, fillChar));
3026     QTest::keyPress(&canvas, '4');
3027     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
3028     QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3029
3030     // Verify the last character entered is replaced by the fill character after a delay.
3031     // Also check the cursor position is updated to accomdate a size difference between
3032     // the fill character and the replaced character.
3033     QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged()));
3034     QTest::qWait(maskDelay);
3035     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
3036     QCOMPARE(cursorSpy.count(), 1);
3037     QCOMPARE(input->cursorRectangle().topLeft(), cursor->pos());
3038
3039     QTest::keyPress(&canvas, '5');
3040     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3041     input->setFocus(false);
3042     QVERIFY(!input->hasFocus());
3043     QCOMPARE(input->displayText(), QString(6, fillChar));
3044     input->setFocus(true);
3045     QTRY_VERIFY(input->hasFocus());
3046     QCOMPARE(input->displayText(), QString(6, fillChar));
3047     QTest::keyPress(&canvas, '6');
3048     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3049
3050     QInputMethodEvent ev;
3051     ev.setCommitString(QLatin1String("7"));
3052     QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev);
3053     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3054
3055     input->setCursorPosition(3);
3056     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3057     QTest::keyPress(&canvas, 'a');
3058     QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3059     QTest::keyPress(&canvas, Qt::Key_Backspace);
3060     QCOMPARE(input->displayText(), QString(8, fillChar));
3061 }
3062
3063
3064 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3065 {
3066     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3067     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3068
3069     QGuiApplication::sendEvent(view, &press);
3070     QGuiApplication::sendEvent(view, &release);
3071 }
3072
3073
3074 void tst_qquicktextinput::focusOnPress()
3075 {
3076     QString componentStr =
3077             "import QtQuick 2.0\n"
3078             "TextInput {\n"
3079                 "property bool selectOnFocus: false\n"
3080                 "width: 100; height: 50\n"
3081                 "activeFocusOnPress: true\n"
3082                 "text: \"Hello World\"\n"
3083                 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3084             " }";
3085     QQmlComponent texteditComponent(&engine);
3086     texteditComponent.setData(componentStr.toLatin1(), QUrl());
3087     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3088     QVERIFY(textInputObject != 0);
3089     QCOMPARE(textInputObject->focusOnPress(), true);
3090     QCOMPARE(textInputObject->hasFocus(), false);
3091
3092     QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3093     QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3094     QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3095
3096     textInputObject->setFocusOnPress(true);
3097     QCOMPARE(textInputObject->focusOnPress(), true);
3098     QCOMPARE(activeFocusOnPressSpy.count(), 0);
3099
3100     QQuickCanvas canvas;
3101     canvas.resize(100, 50);
3102     textInputObject->setParentItem(canvas.rootItem());
3103     canvas.show();
3104     canvas.requestActivateWindow();
3105     QTest::qWaitForWindowShown(&canvas);
3106     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
3107
3108     QCOMPARE(textInputObject->hasFocus(), false);
3109     QCOMPARE(textInputObject->hasActiveFocus(), false);
3110
3111     QPoint centerPoint(canvas.width()/2, canvas.height()/2);
3112     Qt::KeyboardModifiers noModifiers = 0;
3113     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3114     QGuiApplication::processEvents();
3115     QCOMPARE(textInputObject->hasFocus(), true);
3116     QCOMPARE(textInputObject->hasActiveFocus(), true);
3117     QCOMPARE(focusSpy.count(), 1);
3118     QCOMPARE(activeFocusSpy.count(), 1);
3119     QCOMPARE(textInputObject->selectedText(), QString());
3120     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3121
3122     textInputObject->setFocusOnPress(false);
3123     QCOMPARE(textInputObject->focusOnPress(), false);
3124     QCOMPARE(activeFocusOnPressSpy.count(), 1);
3125
3126     textInputObject->setFocus(false);
3127     QCOMPARE(textInputObject->hasFocus(), false);
3128     QCOMPARE(textInputObject->hasActiveFocus(), false);
3129     QCOMPARE(focusSpy.count(), 2);
3130     QCOMPARE(activeFocusSpy.count(), 2);
3131
3132     // Wait for double click timeout to expire before clicking again.
3133     QTest::qWait(400);
3134     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3135     QGuiApplication::processEvents();
3136     QCOMPARE(textInputObject->hasFocus(), false);
3137     QCOMPARE(textInputObject->hasActiveFocus(), false);
3138     QCOMPARE(focusSpy.count(), 2);
3139     QCOMPARE(activeFocusSpy.count(), 2);
3140     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3141
3142     textInputObject->setFocusOnPress(true);
3143     QCOMPARE(textInputObject->focusOnPress(), true);
3144     QCOMPARE(activeFocusOnPressSpy.count(), 2);
3145
3146     // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3147     textInputObject->setProperty("selectOnFocus", true);
3148
3149     QTest::qWait(400);
3150     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3151     QGuiApplication::processEvents();
3152     QCOMPARE(textInputObject->hasFocus(), true);
3153     QCOMPARE(textInputObject->hasActiveFocus(), true);
3154     QCOMPARE(focusSpy.count(), 3);
3155     QCOMPARE(activeFocusSpy.count(), 3);
3156     QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3157     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3158 }
3159
3160 void tst_qquicktextinput::openInputPanel()
3161 {
3162     PlatformInputContext platformInputContext;
3163     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3164     inputMethodPrivate->testContext = &platformInputContext;
3165
3166     QQuickView view(testFileUrl("openInputPanel.qml"));
3167     view.show();
3168     view.requestActivateWindow();
3169     QTest::qWaitForWindowShown(&view);
3170     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3171
3172     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3173     QVERIFY(input);
3174
3175     // check default values
3176     QVERIFY(input->focusOnPress());
3177     QVERIFY(!input->hasActiveFocus());
3178     QVERIFY(qApp->focusObject() != input);
3179     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3180
3181     // input panel should open on focus
3182     QPoint centerPoint(view.width()/2, view.height()/2);
3183     Qt::KeyboardModifiers noModifiers = 0;
3184     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3185     QGuiApplication::processEvents();
3186     QVERIFY(input->hasActiveFocus());
3187     QCOMPARE(qApp->focusObject(), input);
3188     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3189     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3190
3191     // input panel should be re-opened when pressing already focused TextInput
3192     qApp->inputMethod()->hide();
3193     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3194     QVERIFY(input->hasActiveFocus());
3195     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3196     QGuiApplication::processEvents();
3197     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3198     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3199
3200     // input panel should stay visible if focus is lost to another text inputor
3201     QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3202     QQuickTextInput anotherInput;
3203     anotherInput.componentComplete();
3204     anotherInput.setParentItem(view.rootObject());
3205     anotherInput.setFocus(true);
3206     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3207     QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3208     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3209
3210     anotherInput.setFocus(false);
3211     QVERIFY(qApp->focusObject() != &anotherInput);
3212     QCOMPARE(view.activeFocusItem(), view.rootItem());
3213     anotherInput.setFocus(true);
3214
3215     qApp->inputMethod()->hide();
3216
3217     // input panel should not be opened if TextInput is read only
3218     input->setReadOnly(true);
3219     input->setFocus(true);
3220     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3221     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3222     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3223     QGuiApplication::processEvents();
3224     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3225
3226     // input panel should not be opened if focusOnPress is set to false
3227     input->setFocusOnPress(false);
3228     input->setFocus(false);
3229     input->setFocus(true);
3230     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3231     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3232     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3233     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3234 }
3235
3236 class MyTextInput : public QQuickTextInput
3237 {
3238 public:
3239     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3240     {
3241         nbPaint = 0;
3242     }
3243     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3244     {
3245        nbPaint++;
3246        return QQuickTextInput::updatePaintNode(node, data);
3247     }
3248     int nbPaint;
3249 };
3250
3251 void tst_qquicktextinput::setHAlignClearCache()
3252 {
3253     QQuickView view;
3254     MyTextInput input;
3255     input.setText("Hello world");
3256     input.setParentItem(view.rootItem());
3257     view.show();
3258     view.requestActivateWindow();
3259     QTest::qWaitForWindowShown(&view);
3260 #ifdef Q_OS_MAC
3261     QEXPECT_FAIL("", "QTBUG-23485", Abort);
3262 #endif
3263     QTRY_COMPARE(input.nbPaint, 1);
3264     input.setHAlign(QQuickTextInput::AlignRight);
3265     //Changing the alignment should trigger a repaint
3266     QTRY_COMPARE(input.nbPaint, 2);
3267 }
3268
3269 void tst_qquicktextinput::focusOutClearSelection()
3270 {
3271     QQuickView view;
3272     QQuickTextInput input;
3273     QQuickTextInput input2;
3274     input.setText(QLatin1String("Hello world"));
3275     input.setFocus(true);
3276     input2.setParentItem(view.rootItem());
3277     input.setParentItem(view.rootItem());
3278     input.componentComplete();
3279     input2.componentComplete();
3280     view.show();
3281     view.requestActivateWindow();
3282     QTest::qWaitForWindowShown(&view);
3283     input.select(2,5);
3284     //The selection should work
3285     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3286     input2.setFocus(true);
3287     QGuiApplication::processEvents();
3288     //The input lost the focus selection should be cleared
3289     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3290 }
3291
3292 void tst_qquicktextinput::geometrySignals()
3293 {
3294     QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3295     QObject *o = component.create();
3296     QVERIFY(o);
3297     QCOMPARE(o->property("bindingWidth").toInt(), 400);
3298     QCOMPARE(o->property("bindingHeight").toInt(), 500);
3299     delete o;
3300 }
3301
3302 void tst_qquicktextinput::contentSize()
3303 {
3304     QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3305     QQmlComponent textComponent(&engine);
3306     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3307     QScopedPointer<QObject> object(textComponent.create());
3308     QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3309
3310     QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3311
3312     textObject->setText("The quick red fox jumped over the lazy brown dog");
3313
3314     QVERIFY(textObject->contentWidth() > textObject->width());
3315     QVERIFY(textObject->contentHeight() < textObject->height());
3316     QCOMPARE(spy.count(), 1);
3317
3318     textObject->setWrapMode(QQuickTextInput::WordWrap);
3319     QVERIFY(textObject->contentWidth() <= textObject->width());
3320     QVERIFY(textObject->contentHeight() > textObject->height());
3321     QCOMPARE(spy.count(), 2);
3322
3323     textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3324
3325     QVERIFY(textObject->contentWidth() > textObject->width());
3326     QVERIFY(textObject->contentHeight() > textObject->height());
3327     QCOMPARE(spy.count(), 3);
3328
3329     textObject->setText("The quick red fox jumped over the lazy brown dog");
3330     for (int w = 60; w < 120; ++w) {
3331         textObject->setWidth(w);
3332         QVERIFY(textObject->contentWidth() <= textObject->width());
3333         QVERIFY(textObject->contentHeight() > textObject->height());
3334     }
3335 }
3336
3337 static void sendPreeditText(const QString &text, int cursor)
3338 {
3339     QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3340             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3341     QCoreApplication::sendEvent(qGuiApp->focusObject(), &event);
3342 }
3343
3344 void tst_qquicktextinput::preeditAutoScroll()
3345 {
3346     QString preeditText = "califragisiticexpialidocious!";
3347
3348     QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3349     view.show();
3350     view.requestActivateWindow();
3351     QTest::qWaitForWindowShown(&view);
3352     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3353     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3354     QVERIFY(input);
3355     QVERIFY(input->hasActiveFocus());
3356
3357     input->setWidth(input->implicitWidth());
3358
3359     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3360     int cursorRectangleChanges = 0;
3361
3362     // test the text is scrolled so the preedit is visible.
3363     sendPreeditText(preeditText.mid(0, 3), 1);
3364     QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3365     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3366     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3367
3368     // test the text is scrolled back when the preedit is removed.
3369     QInputMethodEvent imEvent;
3370     QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3371     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3372     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3373     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3374
3375     QTextLayout layout(preeditText);
3376     layout.setFont(input->font());
3377     if (!qmlDisableDistanceField()) {
3378         QTextOption option;
3379         option.setUseDesignMetrics(true);
3380         layout.setTextOption(option);
3381     }
3382     layout.beginLayout();
3383     QTextLine line = layout.createLine();
3384     layout.endLayout();
3385
3386     // test if the preedit is larger than the text input that the
3387     // character preceding the cursor is still visible.
3388     qreal x = input->positionToRectangle(0).x();
3389     for (int i = 0; i < 3; ++i) {
3390         sendPreeditText(preeditText, i + 1);
3391         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3392         QVERIFY(input->cursorRectangle().right() >= width - 3);
3393         QVERIFY(input->positionToRectangle(0).x() < x);
3394         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3395         x = input->positionToRectangle(0).x();
3396     }
3397     for (int i = 1; i >= 0; --i) {
3398         sendPreeditText(preeditText, i + 1);
3399         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3400         QVERIFY(input->cursorRectangle().right() >= width - 3);
3401         QVERIFY(input->positionToRectangle(0).x() > x);
3402         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3403         x = input->positionToRectangle(0).x();
3404     }
3405
3406     // Test incrementing the preedit cursor doesn't cause further
3407     // scrolling when right most text is visible.
3408     sendPreeditText(preeditText, preeditText.length() - 3);
3409     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3410     x = input->positionToRectangle(0).x();
3411     for (int i = 2; i >= 0; --i) {
3412         sendPreeditText(preeditText, preeditText.length() - i);
3413         QCOMPARE(input->positionToRectangle(0).x(), x);
3414         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3415     }
3416     for (int i = 1; i <  3; ++i) {
3417         sendPreeditText(preeditText, preeditText.length() - i);
3418         QCOMPARE(input->positionToRectangle(0).x(), x);
3419         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3420     }
3421
3422     // Test disabling auto scroll.
3423     QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3424
3425     input->setAutoScroll(false);
3426     sendPreeditText(preeditText.mid(0, 3), 1);
3427     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3428     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3429 }
3430
3431 void tst_qquicktextinput::preeditCursorRectangle()
3432 {
3433     QString preeditText = "super";
3434
3435     QQuickView view(testFileUrl("inputMethodEvent.qml"));
3436     view.show();
3437     view.requestActivateWindow();
3438     QTest::qWaitForWindowShown(&view);
3439     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3440     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3441     QVERIFY(input);
3442
3443     QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3444     QVERIFY(cursor);
3445
3446     QRectF currentRect;
3447
3448     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3449     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3450     QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3451
3452     // Verify that the micro focus rect is positioned the same for position 0 as
3453     // it would be if there was no preedit text.
3454     sendPreeditText(preeditText, 0);
3455     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3456     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3457     QCOMPARE(currentRect, previousRect);
3458     QCOMPARE(input->cursorRectangle(), currentRect);
3459     QCOMPARE(cursor->pos(), currentRect.topLeft());
3460
3461     QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3462     QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3463
3464     // Verify that the micro focus rect moves to the left as the cursor position
3465     // is incremented.
3466     for (int i = 1; i <= 5; ++i) {
3467         sendPreeditText(preeditText, i);
3468         QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3469         currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3470         QVERIFY(previousRect.left() < currentRect.left());
3471         QCOMPARE(input->cursorRectangle(), currentRect);
3472         QCOMPARE(cursor->pos(), currentRect.topLeft());
3473         QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3474         QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3475         previousRect = currentRect;
3476     }
3477
3478     // Verify that if the cursor rectangle is updated if the pre-edit text changes
3479     // but the (non-zero) cursor position is the same.
3480     inputSpy.clear();
3481     panelSpy.clear();
3482     sendPreeditText("wwwww", 5);
3483     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3484     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3485     QCOMPARE(input->cursorRectangle(), currentRect);
3486     QCOMPARE(cursor->pos(), currentRect.topLeft());
3487     QCOMPARE(inputSpy.count(), 1);
3488     QCOMPARE(panelSpy.count(), 1);
3489
3490     // Verify that if there is no preedit cursor then the micro focus rect is the
3491     // same as it would be if it were positioned at the end of the preedit text.
3492     inputSpy.clear();
3493     panelSpy.clear();
3494     {   QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3495         QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
3496     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3497     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3498     QCOMPARE(currentRect, previousRect);
3499     QCOMPARE(input->cursorRectangle(), currentRect);
3500     QCOMPARE(cursor->pos(), currentRect.topLeft());
3501     QCOMPARE(inputSpy.count(), 1);
3502     QCOMPARE(panelSpy.count(), 1);
3503 }
3504
3505 void tst_qquicktextinput::inputContextMouseHandler()
3506 {
3507     PlatformInputContext platformInputContext;
3508     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3509     inputMethodPrivate->testContext = &platformInputContext;
3510
3511     QString text = "supercalifragisiticexpialidocious!";
3512     QQuickView view(testFileUrl("inputContext.qml"));
3513     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3514     QVERIFY(input);
3515
3516     input->setFocus(true);
3517     input->setText("");
3518
3519     view.show();
3520     view.requestActivateWindow();
3521     QTest::qWaitForWindowShown(&view);
3522     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3523
3524     QTextLayout layout(text);
3525     layout.setFont(input->font());
3526     if (!qmlDisableDistanceField()) {
3527         QTextOption option;
3528         option.setUseDesignMetrics(true);
3529         layout.setTextOption(option);
3530     }
3531     layout.beginLayout();
3532     QTextLine line = layout.createLine();
3533     layout.endLayout();
3534
3535     const qreal x = line.cursorToX(2, QTextLine::Leading);
3536     const qreal y = line.height() / 2;
3537     QPoint position = QPointF(x, y).toPoint();
3538
3539     QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3540     QGuiApplication::sendEvent(input, &inputEvent);
3541
3542     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3543     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3544     QGuiApplication::processEvents();
3545
3546     QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3547     QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3548     QCOMPARE(platformInputContext.m_cursorPosition, 2);
3549 }
3550
3551 void tst_qquicktextinput::inputMethodComposing()
3552 {
3553     QString text = "supercalifragisiticexpialidocious!";
3554
3555     QQuickView view(testFileUrl("inputContext.qml"));
3556     view.show();
3557     view.requestActivateWindow();
3558     QTest::qWaitForWindowShown(&view);
3559     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3560     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3561     QVERIFY(input);
3562     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3563
3564     QCOMPARE(input->isInputMethodComposing(), false);
3565     {
3566         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3567         QGuiApplication::sendEvent(input, &event);
3568     }
3569     QCOMPARE(input->isInputMethodComposing(), true);
3570     QCOMPARE(spy.count(), 1);
3571
3572     {
3573         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3574         QGuiApplication::sendEvent(input, &event);
3575     }
3576     QCOMPARE(spy.count(), 1);
3577
3578     {
3579         QInputMethodEvent event;
3580         QGuiApplication::sendEvent(input, &event);
3581     }
3582     QCOMPARE(input->isInputMethodComposing(), false);
3583     QCOMPARE(spy.count(), 2);
3584
3585     // Changing the text while not composing doesn't alter the composing state.
3586     input->setText(text.mid(0, 16));
3587     QCOMPARE(input->isInputMethodComposing(), false);
3588     QCOMPARE(spy.count(), 2);
3589
3590     {
3591         QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3592         QGuiApplication::sendEvent(input, &event);
3593     }
3594     QCOMPARE(input->isInputMethodComposing(), true);
3595     QCOMPARE(spy.count(), 3);
3596
3597     // Changing the text while composing cancels composition.
3598     input->setText(text.mid(0, 12));
3599     QCOMPARE(input->isInputMethodComposing(), false);
3600     QCOMPARE(spy.count(), 4);
3601
3602     {   // Preedit cursor positioned outside (empty) preedit; composing.
3603         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3604                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3605         QGuiApplication::sendEvent(input, &event);
3606     }
3607     QCOMPARE(input->isInputMethodComposing(), true);
3608     QCOMPARE(spy.count(), 5);
3609
3610
3611     {   // Cursor hidden; composing
3612         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3613                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3614         QGuiApplication::sendEvent(input, &event);
3615     }
3616     QCOMPARE(input->isInputMethodComposing(), true);
3617     QCOMPARE(spy.count(), 5);
3618
3619     {   // Default cursor attributes; composing.
3620         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3621                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3622         QGuiApplication::sendEvent(input, &event);
3623     }
3624     QCOMPARE(input->isInputMethodComposing(), true);
3625     QCOMPARE(spy.count(), 5);
3626
3627     {   // Selections are persisted: not composing
3628         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3629                 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3630         QGuiApplication::sendEvent(input, &event);
3631     }
3632     QCOMPARE(input->isInputMethodComposing(), false);
3633     QCOMPARE(spy.count(), 6);
3634
3635     input->setCursorPosition(12);
3636
3637     {   // Formatting applied; composing.
3638         QTextCharFormat format;
3639         format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3640         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3641                 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3642         QGuiApplication::sendEvent(input, &event);
3643     }
3644     QCOMPARE(input->isInputMethodComposing(), true);
3645     QCOMPARE(spy.count(), 7);
3646
3647     {
3648         QInputMethodEvent event;
3649         QGuiApplication::sendEvent(input, &event);
3650     }
3651     QCOMPARE(input->isInputMethodComposing(), false);
3652     QCOMPARE(spy.count(), 8);
3653 }
3654
3655 void tst_qquicktextinput::inputMethodUpdate()
3656 {
3657     PlatformInputContext platformInputContext;
3658     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3659     inputMethodPrivate->testContext = &platformInputContext;
3660
3661     QQuickView view(testFileUrl("inputContext.qml"));
3662     view.show();
3663     view.requestActivateWindow();
3664     QTest::qWaitForWindowShown(&view);
3665     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3666     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3667     QVERIFY(input);
3668
3669     // text change even without cursor position change needs to trigger update
3670     input->setText("test");
3671     platformInputContext.clear();
3672     input->setText("xxxx");
3673     QVERIFY(platformInputContext.m_updateCallCount > 0);
3674
3675     // input method event replacing text
3676     platformInputContext.clear();
3677     {
3678         QInputMethodEvent inputMethodEvent;
3679         inputMethodEvent.setCommitString("y", -1, 1);
3680         QGuiApplication::sendEvent(input, &inputMethodEvent);
3681     }
3682     QVERIFY(platformInputContext.m_updateCallCount > 0);
3683
3684     // input method changing selection
3685     platformInputContext.clear();
3686     {
3687         QList<QInputMethodEvent::Attribute> attributes;
3688         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3689         QInputMethodEvent inputMethodEvent("", attributes);
3690         QGuiApplication::sendEvent(input, &inputMethodEvent);
3691     }
3692     QVERIFY(input->selectionStart() != input->selectionEnd());
3693     QVERIFY(platformInputContext.m_updateCallCount > 0);
3694
3695     // programmatical selections trigger update
3696     platformInputContext.clear();
3697     input->selectAll();
3698     QVERIFY(platformInputContext.m_updateCallCount > 0);
3699
3700     // font changes
3701     platformInputContext.clear();
3702     QFont font = input->font();
3703     font.setBold(!font.bold());
3704     input->setFont(font);
3705     QVERIFY(platformInputContext.m_updateCallCount > 0);
3706
3707     // normal input
3708     platformInputContext.clear();
3709     {
3710         QInputMethodEvent inputMethodEvent;
3711         inputMethodEvent.setCommitString("y");
3712         QGuiApplication::sendEvent(input, &inputMethodEvent);
3713     }
3714     QVERIFY(platformInputContext.m_updateCallCount > 0);
3715
3716     // changing cursor position
3717     platformInputContext.clear();
3718     input->setCursorPosition(0);
3719     QVERIFY(platformInputContext.m_updateCallCount > 0);
3720
3721     // read only disabled input method
3722     platformInputContext.clear();
3723     input->setReadOnly(true);
3724     QVERIFY(platformInputContext.m_updateCallCount > 0);
3725     input->setReadOnly(false);
3726
3727     // no updates while no focus
3728     input->setFocus(false);
3729     platformInputContext.clear();
3730     input->setText("Foo");
3731     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3732     input->setCursorPosition(1);
3733     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3734     input->selectAll();
3735     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3736     input->setReadOnly(true);
3737     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3738 }
3739
3740 void tst_qquicktextinput::cursorRectangleSize()
3741 {
3742     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3743     QVERIFY(canvas->rootObject() != 0);
3744     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3745
3746     // make sure cursor rectangle is not at (0,0)
3747     textInput->setX(10);
3748     textInput->setY(10);
3749     textInput->setCursorPosition(3);
3750     QVERIFY(textInput != 0);
3751     textInput->setFocus(true);
3752     canvas->show();
3753     canvas->requestActivateWindow();
3754     QTest::qWaitForWindowShown(canvas);
3755     QTRY_VERIFY(qApp->focusObject());
3756
3757     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3758     qApp->sendEvent(qApp->focusObject(), &event);
3759     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3760
3761     QRectF cursorRectFromItem = textInput->cursorRectangle();
3762     QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3763
3764     // item and input query cursor rectangles match
3765     QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3766
3767     // item cursor rectangle and positionToRectangle calculations match
3768     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3769
3770     // item-canvas transform and input item transform match
3771     QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3772
3773     // input panel cursorRectangle property and tranformed item cursor rectangle match
3774     QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3775     QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3776
3777     delete canvas;
3778 }
3779
3780 void tst_qquicktextinput::tripleClickSelectsAll()
3781 {
3782     QString qmlfile = testFile("positionAt.qml");
3783     QQuickView view(QUrl::fromLocalFile(qmlfile));
3784     view.show();
3785     view.requestActivateWindow();
3786     QTest::qWaitForWindowShown(&view);
3787
3788     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3789
3790     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3791     QVERIFY(input);
3792
3793     QLatin1String hello("Hello world!");
3794     input->setSelectByMouse(true);
3795     input->setText(hello);
3796
3797     // Clicking on the same point inside TextInput three times in a row
3798     // should trigger a triple click, thus selecting all the text.
3799     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3800     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3801     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3802     QGuiApplication::processEvents();
3803     QCOMPARE(input->selectedText(), hello);
3804
3805     // Now it simulates user moving the mouse between the second and the third click.
3806     // In this situation, we don't expect a triple click.
3807     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3808     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3809     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3810     QGuiApplication::processEvents();
3811     QVERIFY(input->selectedText().isEmpty());
3812
3813     // And now we press the third click too late, so no triple click event is triggered.
3814     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3815     QGuiApplication::processEvents();
3816     QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3817     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3818     QGuiApplication::processEvents();
3819     QVERIFY(input->selectedText().isEmpty());
3820 }
3821
3822 void tst_qquicktextinput::QTBUG_19956_data()
3823 {
3824     QTest::addColumn<QString>("url");
3825     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3826     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3827 }
3828
3829
3830 void tst_qquicktextinput::getText_data()
3831 {
3832     QTest::addColumn<QString>("text");
3833     QTest::addColumn<QString>("inputMask");
3834     QTest::addColumn<int>("start");
3835     QTest::addColumn<int>("end");
3836     QTest::addColumn<QString>("expectedText");
3837
3838     QTest::newRow("all plain text")
3839             << standard.at(0)
3840             << QString()
3841             << 0 << standard.at(0).length()
3842             << standard.at(0);
3843
3844     QTest::newRow("plain text sub string")
3845             << standard.at(0)
3846             << QString()
3847             << 0 << 12
3848             << standard.at(0).mid(0, 12);
3849
3850     QTest::newRow("plain text sub string reversed")
3851             << standard.at(0)
3852             << QString()
3853             << 12 << 0
3854             << standard.at(0).mid(0, 12);
3855
3856     QTest::newRow("plain text cropped beginning")
3857             << standard.at(0)
3858             << QString()
3859             << -3 << 4
3860             << standard.at(0).mid(0, 4);
3861
3862     QTest::newRow("plain text cropped end")
3863             << standard.at(0)
3864             << QString()
3865             << 23 << standard.at(0).length() + 8
3866             << standard.at(0).mid(23);
3867
3868     QTest::newRow("plain text cropped beginning and end")
3869             << standard.at(0)
3870             << QString()
3871             << -9 << standard.at(0).length() + 4
3872             << standard.at(0);
3873 }
3874
3875 void tst_qquicktextinput::getText()
3876 {
3877     QFETCH(QString, text);
3878     QFETCH(QString, inputMask);
3879     QFETCH(int, start);
3880     QFETCH(int, end);
3881     QFETCH(QString, expectedText);
3882
3883     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3884     QQmlComponent textInputComponent(&engine);
3885     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3886     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3887     QVERIFY(textInput != 0);
3888
3889     QCOMPARE(textInput->getText(start, end), expectedText);
3890 }
3891
3892 void tst_qquicktextinput::insert_data()
3893 {
3894     QTest::addColumn<QString>("text");
3895     QTest::addColumn<QString>("inputMask");
3896     QTest::addColumn<int>("selectionStart");
3897     QTest::addColumn<int>("selectionEnd");
3898     QTest::addColumn<int>("insertPosition");
3899     QTest::addColumn<QString>("insertText");
3900     QTest::addColumn<QString>("expectedText");
3901     QTest::addColumn<int>("expectedSelectionStart");
3902     QTest::addColumn<int>("expectedSelectionEnd");
3903     QTest::addColumn<int>("expectedCursorPosition");
3904     QTest::addColumn<bool>("selectionChanged");
3905     QTest::addColumn<bool>("cursorPositionChanged");
3906
3907     QTest::newRow("at cursor position (beginning)")
3908             << standard.at(0)
3909             << QString()
3910             << 0 << 0 << 0
3911             << QString("Hello")
3912             << QString("Hello") + standard.at(0)
3913             << 5 << 5 << 5
3914             << false << true;
3915
3916     QTest::newRow("at cursor position (end)")
3917             << standard.at(0)
3918             << QString()
3919             << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3920             << QString("Hello")
3921             << standard.at(0) + QString("Hello")
3922             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3923             << false << true;
3924
3925     QTest::newRow("at cursor position (middle)")
3926             << standard.at(0)
3927             << QString()
3928             << 18 << 18 << 18
3929             << QString("Hello")
3930             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3931             << 23 << 23 << 23
3932             << false << true;
3933
3934     QTest::newRow("after cursor position (beginning)")
3935             << standard.at(0)
3936             << QString()
3937             << 0 << 0 << 18
3938             << QString("Hello")
3939             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3940             << 0 << 0 << 0
3941             << false << false;
3942
3943     QTest::newRow("before cursor position (end)")
3944             << standard.at(0)
3945             << QString()
3946             << standard.at(0).length() << standard.at(0).length() << 18
3947             << QString("Hello")
3948             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3949             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3950             << false << true;
3951
3952     QTest::newRow("before cursor position (middle)")
3953             << standard.at(0)
3954             << QString()
3955             << 18 << 18 << 0
3956             << QString("Hello")
3957             << QString("Hello") + standard.at(0)
3958             << 23 << 23 << 23
3959             << false << true;
3960
3961     QTest::newRow("after cursor position (middle)")
3962             << standard.at(0)
3963             << QString()
3964             << 18 << 18 << standard.at(0).length()
3965             << QString("Hello")
3966             << standard.at(0) + QString("Hello")
3967             << 18 << 18 << 18
3968             << false << false;
3969
3970     QTest::newRow("before selection")
3971             << standard.at(0)
3972             << QString()
3973             << 14 << 19 << 0
3974             << QString("Hello")
3975             << QString("Hello") + standard.at(0)
3976             << 19 << 24 << 24
3977             << false << true;
3978
3979     QTest::newRow("before reversed selection")
3980             << standard.at(0)
3981             << QString()
3982             << 19 << 14 << 0
3983             << QString("Hello")
3984             << QString("Hello") + standard.at(0)
3985             << 19 << 24 << 19
3986             << false << true;
3987
3988     QTest::newRow("after selection")
3989             << standard.at(0)
3990             << QString()
3991             << 14 << 19 << standard.at(0).length()
3992             << QString("Hello")
3993             << standard.at(0) + QString("Hello")
3994             << 14 << 19 << 19
3995             << false << false;
3996
3997     QTest::newRow("after reversed selection")
3998             << standard.at(0)
3999             << QString()
4000             << 19 << 14 << standard.at(0).length()
4001             << QString("Hello")
4002             << standard.at(0) + QString("Hello")
4003             << 14 << 19 << 14
4004             << false << false;
4005
4006     QTest::newRow("into selection")
4007             << standard.at(0)
4008             << QString()
4009             << 14 << 19 << 18
4010             << QString("Hello")
4011             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4012             << 14 << 24 << 24
4013             << true << true;
4014
4015     QTest::newRow("into reversed selection")
4016             << standard.at(0)
4017             << QString()
4018             << 19 << 14 << 18
4019             << QString("Hello")
4020             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
4021             << 14 << 24 << 14
4022             << true << false;
4023
4024     QTest::newRow("rich text into plain text")
4025             << standard.at(0)
4026             << QString()
4027             << 0 << 0 << 0
4028             << QString("<b>Hello</b>")
4029             << QString("<b>Hello</b>") + standard.at(0)
4030             << 12 << 12 << 12
4031             << false << true;
4032
4033     QTest::newRow("before start")
4034             << standard.at(0)
4035             << QString()
4036             << 0 << 0 << -3
4037             << QString("Hello")
4038             << standard.at(0)
4039             << 0 << 0 << 0
4040             << false << false;
4041
4042     QTest::newRow("past end")
4043             << standard.at(0)
4044             << QString()
4045             << 0 << 0 << standard.at(0).length() + 3
4046             << QString("Hello")
4047             << standard.at(0)
4048             << 0 << 0 << 0
4049             << false << false;
4050
4051     const QString inputMask = "009.009.009.009";
4052     const QString ip = "192.168.2.14";
4053
4054     QTest::newRow("mask: at cursor position (beginning)")
4055             << ip
4056             << inputMask
4057             << 0 << 0 << 0
4058             << QString("125")
4059             << QString("125.168.2.14")
4060             << 0 << 0 << 0
4061             << false << false;
4062
4063     QTest::newRow("mask: at cursor position (end)")
4064             << ip
4065             << inputMask
4066             << inputMask.length() << inputMask.length() << inputMask.length()
4067             << QString("8")
4068             << ip
4069             << inputMask.length() << inputMask.length() << inputMask.length()
4070             << false << false;
4071
4072     QTest::newRow("mask: at cursor position (middle)")
4073             << ip
4074             << inputMask
4075             << 6 << 6 << 6
4076             << QString("75.2")
4077             << QString("192.167.5.24")
4078             << 6 << 6 << 6
4079             << false << false;
4080
4081     QTest::newRow("mask: after cursor position (beginning)")
4082             << ip
4083             << inputMask
4084             << 0 << 0 << 6
4085             << QString("75.2")
4086             << QString("192.167.5.24")
4087             << 0 << 0 << 0
4088             << false << false;
4089
4090     QTest::newRow("mask: before cursor position (end)")
4091             << ip
4092             << inputMask
4093             << inputMask.length() << inputMask.length() << 6
4094             << QString("75.2")
4095             << QString("192.167.5.24")
4096             << inputMask.length() << inputMask.length() << inputMask.length()
4097             << false << false;
4098
4099     QTest::newRow("mask: before cursor position (middle)")
4100             << ip
4101             << inputMask
4102             << 6 << 6 << 0
4103             << QString("125")
4104             << QString("125.168.2.14")
4105             << 6 << 6 << 6
4106             << false << false;
4107
4108     QTest::newRow("mask: after cursor position (middle)")
4109             << ip
4110             << inputMask
4111             << 6 << 6 << 13
4112             << QString("8")
4113             << "192.168.2.18"
4114             << 6 << 6 << 6
4115             << false << false;
4116
4117     QTest::newRow("mask: before selection")
4118             << ip
4119             << inputMask
4120             << 6 << 8 << 0
4121             << QString("125")
4122             << QString("125.168.2.14")
4123             << 6 << 8 << 8
4124             << false << false;
4125
4126     QTest::newRow("mask: before reversed selection")
4127             << ip
4128             << inputMask
4129             << 8 << 6 << 0
4130             << QString("125")
4131             << QString("125.168.2.14")
4132             << 6 << 8 << 6
4133             << false << false;
4134
4135     QTest::newRow("mask: after selection")
4136             << ip
4137             << inputMask
4138             << 6 << 8 << 13
4139             << QString("8")
4140             << "192.168.2.18"
4141             << 6 << 8 << 8
4142             << false << false;
4143
4144     QTest::newRow("mask: after reversed selection")
4145             << ip
4146             << inputMask
4147             << 8 << 6 << 13
4148             << QString("8")
4149             << "192.168.2.18"
4150             << 6 << 8 << 6
4151             << false << false;
4152
4153     QTest::newRow("mask: into selection")
4154             << ip
4155             << inputMask
4156             << 5 << 8 << 6
4157             << QString("75.2")
4158             << QString("192.167.5.24")
4159             << 5 << 8 << 8
4160             << true << false;
4161
4162     QTest::newRow("mask: into reversed selection")
4163             << ip
4164             << inputMask
4165             << 8 << 5 << 6
4166             << QString("75.2")
4167             << QString("192.167.5.24")
4168             << 5 << 8 << 5
4169             << true << false;
4170
4171     QTest::newRow("mask: before start")
4172             << ip
4173             << inputMask
4174             << 0 << 0 << -3
4175             << QString("4")
4176             << ip
4177             << 0 << 0 << 0
4178             << false << false;
4179
4180     QTest::newRow("mask: past end")
4181             << ip
4182             << inputMask
4183             << 0 << 0 << ip.length() + 3
4184             << QString("4")
4185             << ip
4186             << 0 << 0 << 0
4187             << false << false;
4188
4189     QTest::newRow("mask: invalid characters")
4190             << ip
4191             << inputMask
4192             << 0 << 0 << 0
4193             << QString("abc")
4194             << QString("192.168.2.14")
4195             << 0 << 0 << 0
4196             << false << false;
4197
4198     QTest::newRow("mask: mixed validity")
4199             << ip
4200             << inputMask
4201             << 0 << 0 << 0
4202             << QString("a1b2c5")
4203             << QString("125.168.2.14")
4204             << 0 << 0 << 0
4205             << false << false;
4206 }
4207
4208 void tst_qquicktextinput::insert()
4209 {
4210     QFETCH(QString, text);
4211     QFETCH(QString, inputMask);
4212     QFETCH(int, selectionStart);
4213     QFETCH(int, selectionEnd);
4214     QFETCH(int, insertPosition);
4215     QFETCH(QString, insertText);
4216     QFETCH(QString, expectedText);
4217     QFETCH(int, expectedSelectionStart);
4218     QFETCH(int, expectedSelectionEnd);
4219     QFETCH(int, expectedCursorPosition);
4220     QFETCH(bool, selectionChanged);
4221     QFETCH(bool, cursorPositionChanged);
4222
4223     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4224     QQmlComponent textInputComponent(&engine);
4225     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4226     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4227     QVERIFY(textInput != 0);
4228
4229     textInput->select(selectionStart, selectionEnd);
4230
4231     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4232     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4233     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4234     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4235     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4236
4237     textInput->insert(insertPosition, insertText);
4238
4239     QCOMPARE(textInput->text(), expectedText);
4240     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4241
4242     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4243     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4244     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4245
4246     if (selectionStart > selectionEnd)
4247         qSwap(selectionStart, selectionEnd);
4248
4249     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4250     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4251     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4252     QCOMPARE(textSpy.count() > 0, text != expectedText);
4253     QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4254 }
4255
4256 void tst_qquicktextinput::remove_data()
4257 {
4258     QTest::addColumn<QString>("text");
4259     QTest::addColumn<QString>("inputMask");
4260     QTest::addColumn<int>("selectionStart");
4261     QTest::addColumn<int>("selectionEnd");
4262     QTest::addColumn<int>("removeStart");
4263     QTest::addColumn<int>("removeEnd");
4264     QTest::addColumn<QString>("expectedText");
4265     QTest::addColumn<int>("expectedSelectionStart");
4266     QTest::addColumn<int>("expectedSelectionEnd");
4267     QTest::addColumn<int>("expectedCursorPosition");
4268     QTest::addColumn<bool>("selectionChanged");
4269     QTest::addColumn<bool>("cursorPositionChanged");
4270
4271     QTest::newRow("from cursor position (beginning)")
4272             << standard.at(0)
4273             << QString()
4274             << 0 << 0
4275             << 0 << 5
4276             << standard.at(0).mid(5)
4277             << 0 << 0 << 0
4278             << false << false;
4279
4280     QTest::newRow("to cursor position (beginning)")
4281             << standard.at(0)
4282             << QString()
4283             << 0 << 0
4284             << 5 << 0
4285             << standard.at(0).mid(5)
4286             << 0 << 0 << 0
4287             << false << false;
4288
4289     QTest::newRow("to cursor position (end)")
4290             << standard.at(0)
4291             << QString()
4292             << standard.at(0).length() << standard.at(0).length()
4293             << standard.at(0).length() << standard.at(0).length() - 5
4294             << standard.at(0).mid(0, standard.at(0).length() - 5)
4295             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4296             << false << true;
4297
4298     QTest::newRow("to cursor position (end)")
4299             << standard.at(0)
4300             << QString()
4301             << standard.at(0).length() << standard.at(0).length()
4302             << standard.at(0).length() - 5 << standard.at(0).length()
4303             << standard.at(0).mid(0, standard.at(0).length() - 5)
4304             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4305             << false << true;
4306
4307     QTest::newRow("from cursor position (middle)")
4308             << standard.at(0)
4309             << QString()
4310             << 18 << 18
4311             << 18 << 23
4312             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4313             << 18 << 18 << 18
4314             << false << false;
4315
4316     QTest::newRow("to cursor position (middle)")
4317             << standard.at(0)
4318             << QString()
4319             << 23 << 23
4320             << 18 << 23
4321             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4322             << 18 << 18 << 18
4323             << false << true;
4324
4325     QTest::newRow("after cursor position (beginning)")
4326             << standard.at(0)
4327             << QString()
4328             << 0 << 0
4329             << 18 << 23
4330             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4331             << 0 << 0 << 0
4332             << false << false;
4333
4334     QTest::newRow("before cursor position (end)")
4335             << standard.at(0)
4336             << QString()
4337             << standard.at(0).length() << standard.at(0).length()
4338             << 18 << 23
4339             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4340             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4341             << false << true;
4342
4343     QTest::newRow("before cursor position (middle)")
4344             << standard.at(0)
4345             << QString()
4346             << 23 << 23
4347             << 0 << 5
4348             << standard.at(0).mid(5)
4349             << 18 << 18 << 18
4350             << false << true;
4351
4352     QTest::newRow("after cursor position (middle)")
4353             << standard.at(0)
4354             << QString()
4355             << 18 << 18
4356             << 18 << 23
4357             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4358             << 18 << 18 << 18
4359             << false << false;
4360
4361     QTest::newRow("before selection")
4362             << standard.at(0)
4363             << QString()
4364             << 14 << 19
4365             << 0 << 5
4366             << standard.at(0).mid(5)
4367             << 9 << 14 << 14
4368             << false << true;
4369
4370     QTest::newRow("before reversed selection")
4371             << standard.at(0)
4372             << QString()
4373             << 19 << 14
4374             << 0 << 5
4375             << standard.at(0).mid(5)
4376             << 9 << 14 << 9
4377             << false << true;
4378
4379     QTest::newRow("after selection")
4380             << standard.at(0)
4381             << QString()
4382             << 14 << 19
4383             << standard.at(0).length() - 5 << standard.at(0).length()
4384             << standard.at(0).mid(0, standard.at(0).length() - 5)
4385             << 14 << 19 << 19
4386             << false << false;
4387
4388     QTest::newRow("after reversed selection")
4389             << standard.at(0)
4390             << QString()
4391             << 19 << 14
4392             << standard.at(0).length() - 5 << standard.at(0).length()
4393             << standard.at(0).mid(0, standard.at(0).length() - 5)
4394             << 14 << 19 << 14
4395             << false << false;
4396
4397     QTest::newRow("from selection")
4398             << standard.at(0)
4399             << QString()
4400             << 14 << 24
4401             << 18 << 23
4402             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4403             << 14 << 19 << 19
4404             << true << true;
4405
4406     QTest::newRow("from reversed selection")
4407             << standard.at(0)
4408             << QString()
4409             << 24 << 14
4410             << 18 << 23
4411             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4412             << 14 << 19 << 14
4413             << true << false;
4414
4415     QTest::newRow("cropped beginning")
4416             << standard.at(0)
4417             << QString()
4418             << 0 << 0
4419             << -3 << 4
4420             << standard.at(0).mid(4)
4421             << 0 << 0 << 0
4422             << false << false;
4423
4424     QTest::newRow("cropped end")
4425             << standard.at(0)
4426             << QString()
4427             << 0 << 0
4428             << 23 << standard.at(0).length() + 8
4429             << standard.at(0).mid(0, 23)
4430             << 0 << 0 << 0
4431             << false << false;
4432
4433     QTest::newRow("cropped beginning and end")
4434             << standard.at(0)
4435             << QString()
4436             << 0 << 0
4437             << -9 << standard.at(0).length() + 4
4438             << QString()
4439             << 0 << 0 << 0
4440             << false << false;
4441
4442     const QString inputMask = "009.009.009.009";
4443     const QString ip = "192.168.2.14";
4444
4445     QTest::newRow("mask: from cursor position")
4446             << ip
4447             << inputMask
4448             << 6 << 6
4449             << 6 << 9
4450             << QString("192.16..14")
4451             << 6 << 6 << 6
4452             << false << false;
4453
4454     QTest::newRow("mask: to cursor position")
4455             << ip
4456             << inputMask
4457             << 6 << 6
4458             << 2 << 6
4459             << QString("19.8.2.14")
4460             << 6 << 6 << 6
4461             << false << false;
4462
4463     QTest::newRow("mask: before cursor position")
4464             << ip
4465             << inputMask
4466             << 6 << 6
4467             << 0 << 2
4468             << QString("2.168.2.14")
4469             << 6 << 6 << 6
4470             << false << false;
4471
4472     QTest::newRow("mask: after cursor position")
4473             << ip
4474             << inputMask
4475             << 6 << 6
4476             << 12 << 16
4477             << QString("192.168.2.")
4478             << 6 << 6 << 6
4479             << false << false;
4480
4481     QTest::newRow("mask: before selection")
4482             << ip
4483             << inputMask
4484             << 6 << 8
4485             << 0 << 2
4486             << QString("2.168.2.14")
4487             << 6 << 8 << 8
4488             << false << false;
4489
4490     QTest::newRow("mask: before reversed selection")
4491             << ip
4492             << inputMask
4493             << 8 << 6
4494             << 0 << 2
4495             << QString("2.168.2.14")
4496             << 6 << 8 << 6
4497             << false << false;
4498
4499     QTest::newRow("mask: after selection")
4500             << ip
4501             << inputMask
4502             << 6 << 8
4503             << 12 << 16
4504             << QString("192.168.2.")
4505             << 6 << 8 << 8
4506             << false << false;
4507
4508     QTest::newRow("mask: after reversed selection")
4509             << ip
4510             << inputMask
4511             << 8 << 6
4512             << 12 << 16
4513             << QString("192.168.2.")
4514             << 6 << 8 << 6
4515             << false << false;
4516
4517     QTest::newRow("mask: from selection")
4518             << ip
4519             << inputMask
4520             << 6 << 13
4521             << 8 << 10
4522             << QString("192.168..14")
4523             << 6 << 13 << 13
4524             << true << false;
4525
4526     QTest::newRow("mask: from reversed selection")
4527             << ip
4528             << inputMask
4529             << 13 << 6
4530             << 8 << 10
4531             << QString("192.168..14")
4532             << 6 << 13 << 6
4533             << true << false;
4534
4535     QTest::newRow("mask: cropped beginning")
4536             << ip
4537             << inputMask
4538             << 0 << 0
4539             << -3 << 4
4540             << QString(".168.2.14")
4541             << 0 << 0 << 0
4542             << false << false;
4543
4544     QTest::newRow("mask: cropped end")
4545             << ip
4546             << inputMask
4547             << 0 << 0
4548             << 13 << 28
4549             << QString("192.168.2.1")
4550             << 0 << 0 << 0
4551             << false << false;
4552
4553     QTest::newRow("mask: cropped beginning and end")
4554             << ip
4555             << inputMask
4556             << 0 << 0
4557             << -9 << 28
4558             << QString("...")
4559             << 0 << 0 << 0
4560             << false << false;
4561 }
4562
4563 void tst_qquicktextinput::remove()
4564 {
4565     QFETCH(QString, text);
4566     QFETCH(QString, inputMask);
4567     QFETCH(int, selectionStart);
4568     QFETCH(int, selectionEnd);
4569     QFETCH(int, removeStart);
4570     QFETCH(int, removeEnd);
4571     QFETCH(QString, expectedText);
4572     QFETCH(int, expectedSelectionStart);
4573     QFETCH(int, expectedSelectionEnd);
4574     QFETCH(int, expectedCursorPosition);
4575     QFETCH(bool, selectionChanged);
4576     QFETCH(bool, cursorPositionChanged);
4577
4578     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4579     QQmlComponent textInputComponent(&engine);
4580     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4581     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4582     QVERIFY(textInput != 0);
4583
4584     textInput->select(selectionStart, selectionEnd);
4585
4586     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4587     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4588     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4589     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4590     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4591
4592     textInput->remove(removeStart, removeEnd);
4593
4594     QCOMPARE(textInput->text(), expectedText);
4595     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4596
4597     if (selectionStart > selectionEnd)  //
4598         qSwap(selectionStart, selectionEnd);
4599
4600     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4601     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4602     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4603
4604     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4605     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4606     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4607     QCOMPARE(textSpy.count() > 0, text != expectedText);
4608
4609     if (cursorPositionChanged)  //
4610         QVERIFY(cursorPositionSpy.count() > 0);
4611 }
4612
4613 void tst_qquicktextinput::keySequence_data()
4614 {
4615     QTest::addColumn<QString>("text");
4616     QTest::addColumn<QKeySequence>("sequence");
4617     QTest::addColumn<int>("selectionStart");
4618     QTest::addColumn<int>("selectionEnd");
4619     QTest::addColumn<int>("cursorPosition");
4620     QTest::addColumn<QString>("expectedText");
4621     QTest::addColumn<QString>("selectedText");
4622     QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4623     QTest::addColumn<Qt::Key>("layoutDirection");
4624
4625     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4626
4627     QTest::newRow("select all")
4628             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4629             << 44 << standard.at(0) << standard.at(0)
4630             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4631     QTest::newRow("select start of line")
4632             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4633             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4634             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4635     QTest::newRow("select start of block")
4636             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4637             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4638             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4639     QTest::newRow("select end of line")
4640             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4641             << 44 << standard.at(0) << standard.at(0).mid(5)
4642             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4643     QTest::newRow("select end of document") // ### Not handled.
4644             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4645             << 3 << standard.at(0) << QString()
4646             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4647     QTest::newRow("select end of block")
4648             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4649             << 44 << standard.at(0) << standard.at(0).mid(18)
4650             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4651     QTest::newRow("delete end of line")
4652             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4653             << 24 << standard.at(0).mid(0, 24) << QString()
4654             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4655     QTest::newRow("move to start of line")
4656             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4657             << 0 << standard.at(0) << QString()
4658             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4659     QTest::newRow("move to start of block")
4660             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4661             << 0 << standard.at(0) << QString()
4662             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4663     QTest::newRow("move to next char")
4664             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4665             << 13 << standard.at(0) << QString()
4666             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4667     QTest::newRow("move to previous char (ltr)")
4668             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4669             << 2 << standard.at(0) << QString()
4670             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4671     QTest::newRow("move to previous char (rtl)")
4672             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4673             << 4 << standard.at(0) << QString()
4674             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4675     QTest::newRow("move to previous char with selection")
4676             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4677             << 3 << standard.at(0) << QString()
4678             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4679     QTest::newRow("select next char (ltr)")
4680             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4681             << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4682             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4683     QTest::newRow("select next char (rtl)")
4684             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4685             << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4686             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4687     QTest::newRow("select previous char (ltr)")
4688             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4689             << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4690             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4691     QTest::newRow("select previous char (rtl)")
4692             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4693             << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4694             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4695     QTest::newRow("move to next word (ltr)")
4696             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4697             << 10 << standard.at(0) << QString()
4698             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4699     QTest::newRow("move to next word (rtl)")
4700             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4701             << 4 << standard.at(0) << QString()
4702             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4703     QTest::newRow("move to next word (password,ltr)")
4704             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4705             << 44 << standard.at(0) << QString()
4706             << QQuickTextInput::Password << Qt::Key_Direction_L;
4707     QTest::newRow("move to next word (password,rtl)")
4708             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4709             << 0 << standard.at(0) << QString()
4710             << QQuickTextInput::Password << Qt::Key_Direction_R;
4711     QTest::newRow("move to previous word (ltr)")
4712             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4713             << 4 << standard.at(0) << QString()
4714             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4715     QTest::newRow("move to previous word (rlt)")
4716             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4717             << 10 << standard.at(0) << QString()
4718             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4719     QTest::newRow("move to previous word (password,ltr)")
4720             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4721             << 0 << standard.at(0) << QString()
4722             << QQuickTextInput::Password << Qt::Key_Direction_L;
4723     QTest::newRow("move to previous word (password,rtl)")
4724             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4725             << 44 << standard.at(0) << QString()
4726             << QQuickTextInput::Password << Qt::Key_Direction_R;
4727     QTest::newRow("select next word")
4728             << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4729             << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4730             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4731     QTest::newRow("select previous word")
4732             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4733             << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4734             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4735     QTest::newRow("delete (selection)")
4736             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4737             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4738             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4739     QTest::newRow("delete (no selection)")
4740             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4741             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4742             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4743     QTest::newRow("delete end of word")
4744             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4745             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4746             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4747     QTest::newRow("delete start of word")
4748             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4749             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4750             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4751 }
4752
4753 void tst_qquicktextinput::keySequence()
4754 {
4755     QFETCH(QString, text);
4756     QFETCH(QKeySequence, sequence);
4757     QFETCH(int, selectionStart);
4758     QFETCH(int, selectionEnd);
4759     QFETCH(int, cursorPosition);
4760     QFETCH(QString, expectedText);
4761     QFETCH(QString, selectedText);
4762     QFETCH(QQuickTextInput::EchoMode, echoMode);
4763     QFETCH(Qt::Key, layoutDirection);
4764
4765     if (sequence.isEmpty()) {
4766         QSKIP("Key sequence is undefined");
4767     }
4768
4769     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4770     QQmlComponent textInputComponent(&engine);
4771     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4772     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4773     QVERIFY(textInput != 0);
4774     textInput->setEchoMode(echoMode);
4775
4776     QQuickCanvas canvas;
4777     textInput->setParentItem(canvas.rootItem());
4778     canvas.show();
4779     canvas.requestActivateWindow();
4780     QTest::qWaitForWindowShown(&canvas);
4781     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
4782
4783     simulateKey(&canvas, layoutDirection);
4784
4785     textInput->select(selectionStart, selectionEnd);
4786
4787     simulateKeys(&canvas, sequence);
4788
4789     QCOMPARE(textInput->cursorPosition(), cursorPosition);
4790     QCOMPARE(textInput->text(), expectedText);
4791     QCOMPARE(textInput->selectedText(), selectedText);
4792 }
4793
4794 #define NORMAL 0
4795 #define REPLACE_UNTIL_END 1
4796
4797 void tst_qquicktextinput::undo_data()
4798 {
4799     QTest::addColumn<QStringList>("insertString");
4800     QTest::addColumn<IntList>("insertIndex");
4801     QTest::addColumn<IntList>("insertMode");
4802     QTest::addColumn<QStringList>("expectedString");
4803     QTest::addColumn<bool>("use_keys");
4804
4805     for (int i=0; i<2; i++) {
4806         QString keys_str = "keyboard";
4807         bool use_keys = true;
4808         if (i==0) {
4809             keys_str = "insert";
4810             use_keys = false;
4811         }
4812
4813         {
4814             IntList insertIndex;
4815             IntList insertMode;
4816             QStringList insertString;
4817             QStringList expectedString;
4818
4819             insertIndex << -1;
4820             insertMode << NORMAL;
4821             insertString << "1";
4822
4823             insertIndex << -1;
4824             insertMode << NORMAL;
4825             insertString << "5";
4826
4827             insertIndex << 1;
4828             insertMode << NORMAL;
4829             insertString << "3";
4830
4831             insertIndex << 1;
4832             insertMode << NORMAL;
4833             insertString << "2";
4834
4835             insertIndex << 3;
4836             insertMode << NORMAL;
4837             insertString << "4";
4838
4839             expectedString << "12345";
4840             expectedString << "1235";
4841             expectedString << "135";
4842             expectedString << "15";
4843             expectedString << "";
4844
4845             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4846                 insertString <<
4847                 insertIndex <<
4848                 insertMode <<
4849                 expectedString <<
4850                 bool(use_keys);
4851         }
4852         {
4853             IntList insertIndex;
4854             IntList insertMode;
4855             QStringList insertString;
4856             QStringList expectedString;
4857
4858             insertIndex << -1;
4859             insertMode << NORMAL;
4860             insertString << "World"; // World
4861
4862             insertIndex << 0;
4863             insertMode << NORMAL;
4864             insertString << "Hello"; // HelloWorld
4865
4866             insertIndex << 0;
4867             insertMode << NORMAL;
4868             insertString << "Well"; // WellHelloWorld
4869
4870             insertIndex << 9;
4871             insertMode << NORMAL;
4872             insertString << "There"; // WellHelloThereWorld;
4873
4874             expectedString << "WellHelloThereWorld";
4875             expectedString << "WellHelloWorld";
4876             expectedString << "HelloWorld";
4877             expectedString << "World";
4878             expectedString << "";
4879
4880             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4881                 insertString <<
4882                 insertIndex <<
4883                 insertMode <<
4884                 expectedString <<
4885                 bool(use_keys);
4886         }
4887         {
4888             IntList insertIndex;
4889             IntList insertMode;
4890             QStringList insertString;
4891             QStringList expectedString;
4892
4893             insertIndex << -1;
4894             insertMode << NORMAL;
4895             insertString << "Ensuring";
4896
4897             insertIndex << -1;
4898             insertMode << NORMAL;
4899             insertString << " instan";
4900
4901             insertIndex << 9;
4902             insertMode << NORMAL;
4903             insertString << "an ";
4904
4905             insertIndex << 10;
4906             insertMode << REPLACE_UNTIL_END;
4907             insertString << " unique instance.";
4908
4909             expectedString << "Ensuring a unique instance.";
4910             expectedString << "Ensuring an instan";
4911             expectedString << "Ensuring instan";
4912             expectedString << "";
4913
4914             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4915                 insertString <<
4916                 insertIndex <<
4917                 insertMode <<
4918                 expectedString <<
4919                 bool(use_keys);
4920         }
4921     }
4922 }
4923
4924 void tst_qquicktextinput::undo()
4925 {
4926     QFETCH(QStringList, insertString);
4927     QFETCH(IntList, insertIndex);
4928     QFETCH(IntList, insertMode);
4929     QFETCH(QStringList, expectedString);
4930
4931     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4932     QQmlComponent textInputComponent(&engine);
4933     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4934     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4935     QVERIFY(textInput != 0);
4936
4937     QQuickCanvas canvas;
4938     textInput->setParentItem(canvas.rootItem());
4939     canvas.show();
4940     canvas.requestActivateWindow();
4941     QTest::qWaitForWindowShown(&canvas);
4942     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
4943
4944     QVERIFY(!textInput->canUndo());
4945
4946     QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4947
4948     int i;
4949
4950 // STEP 1: First build up an undo history by inserting or typing some strings...
4951     for (i = 0; i < insertString.size(); ++i) {
4952         if (insertIndex[i] > -1)
4953             textInput->setCursorPosition(insertIndex[i]);
4954
4955  // experimental stuff
4956         if (insertMode[i] == REPLACE_UNTIL_END) {
4957             textInput->select(insertIndex[i], insertIndex[i] + 8);
4958
4959             // This is what I actually want...
4960             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4961         }
4962
4963         for (int j = 0; j < insertString.at(i).length(); j++)
4964             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4965     }
4966
4967     QCOMPARE(spy.count(), 1);
4968
4969 // STEP 2: Next call undo several times and see if we can restore to the previous state
4970     for (i = 0; i < expectedString.size() - 1; ++i) {
4971         QCOMPARE(textInput->text(), expectedString[i]);
4972         QVERIFY(textInput->canUndo());
4973         textInput->undo();
4974     }
4975
4976 // STEP 3: Verify that we have undone everything
4977     QVERIFY(textInput->text().isEmpty());
4978     QVERIFY(!textInput->canUndo());
4979     QCOMPARE(spy.count(), 2);
4980 }
4981
4982 void tst_qquicktextinput::redo_data()
4983 {
4984     QTest::addColumn<QStringList>("insertString");
4985     QTest::addColumn<IntList>("insertIndex");
4986     QTest::addColumn<QStringList>("expectedString");
4987
4988     {
4989         IntList insertIndex;
4990         QStringList insertString;
4991         QStringList expectedString;
4992
4993         insertIndex << -1;
4994         insertString << "World"; // World
4995         insertIndex << 0;
4996         insertString << "Hello"; // HelloWorld
4997         insertIndex << 0;
4998         insertString << "Well"; // WellHelloWorld
4999         insertIndex << 9;
5000         insertString << "There"; // WellHelloThereWorld;
5001
5002         expectedString << "World";
5003         expectedString << "HelloWorld";
5004         expectedString << "WellHelloWorld";
5005         expectedString << "WellHelloThereWorld";
5006
5007         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
5008     }
5009 }
5010
5011 void tst_qquicktextinput::redo()
5012 {
5013     QFETCH(QStringList, insertString);
5014     QFETCH(IntList, insertIndex);
5015     QFETCH(QStringList, expectedString);
5016
5017     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5018     QQmlComponent textInputComponent(&engine);
5019     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5020     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5021     QVERIFY(textInput != 0);
5022
5023     QQuickCanvas canvas;
5024     textInput->setParentItem(canvas.rootItem());
5025     canvas.show();
5026     canvas.requestActivateWindow();
5027     QTest::qWaitForWindowShown(&canvas);
5028     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5029
5030     QVERIFY(!textInput->canUndo());
5031     QVERIFY(!textInput->canRedo());
5032
5033     QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5034
5035     int i;
5036     // inserts the diff strings at diff positions
5037     for (i = 0; i < insertString.size(); ++i) {
5038         if (insertIndex[i] > -1)
5039             textInput->setCursorPosition(insertIndex[i]);
5040         for (int j = 0; j < insertString.at(i).length(); j++)
5041             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
5042         QVERIFY(textInput->canUndo());
5043         QVERIFY(!textInput->canRedo());
5044     }
5045
5046     QCOMPARE(spy.count(), 0);
5047
5048     // undo everything
5049     while (!textInput->text().isEmpty()) {
5050         QVERIFY(textInput->canUndo());
5051         textInput->undo();
5052         QVERIFY(textInput->canRedo());
5053     }
5054
5055     QCOMPARE(spy.count(), 1);
5056
5057     for (i = 0; i < expectedString.size(); ++i) {
5058         QVERIFY(textInput->canRedo());
5059         textInput->redo();
5060         QCOMPARE(textInput->text() , expectedString[i]);
5061         QVERIFY(textInput->canUndo());
5062     }
5063     QVERIFY(!textInput->canRedo());
5064     QCOMPARE(spy.count(), 2);
5065 }
5066
5067 void tst_qquicktextinput::undo_keypressevents_data()
5068 {
5069     QTest::addColumn<KeyList>("keys");
5070     QTest::addColumn<QStringList>("expectedString");
5071
5072     {
5073         KeyList keys;
5074         QStringList expectedString;
5075
5076         keys << "AFRAID"
5077                 << QKeySequence::MoveToStartOfLine
5078                 << "VERY"
5079                 << Qt::Key_Left
5080                 << Qt::Key_Left
5081                 << Qt::Key_Left
5082                 << Qt::Key_Left
5083                 << "BE"
5084                 << QKeySequence::MoveToEndOfLine
5085                 << "!";
5086
5087         expectedString << "BEVERYAFRAID!";
5088         expectedString << "BEVERYAFRAID";
5089         expectedString << "VERYAFRAID";
5090         expectedString << "AFRAID";
5091
5092         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5093     } {
5094         KeyList keys;
5095         QStringList expectedString;
5096
5097         // inserting '1234'
5098         keys << "1234" << QKeySequence::MoveToStartOfLine
5099                 // skipping '12'
5100                 << Qt::Key_Right << Qt::Key_Right
5101                 // selecting '34'
5102                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5103                 // deleting '34'
5104                 << Qt::Key_Delete;
5105
5106         expectedString << "12";
5107         expectedString << "1234";
5108
5109         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5110     } {
5111         KeyList keys;
5112         QStringList expectedString;
5113
5114         // inserting 'AB12'
5115         keys << "AB12"
5116                 << QKeySequence::MoveToStartOfLine
5117                 // selecting 'AB'
5118                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5119                 << Qt::Key_Delete
5120                 << QKeySequence::Undo
5121                 << Qt::Key_Right
5122                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5123                 << Qt::Key_Delete;
5124
5125         expectedString << "AB";
5126         expectedString << "AB12";
5127
5128         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5129     } {
5130         KeyList keys;
5131         QStringList expectedString;
5132
5133         // inserting 'ABCD'
5134         keys << "abcd"
5135                 //move left two
5136                 << Qt::Key_Left << Qt::Key_Left
5137                 // inserting '1234'
5138                 << "1234"
5139                 // selecting '1234'
5140                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5141                 // overwriting '1234' with '5'
5142                 << "5"
5143                 // undoing deletion of 'AB'
5144                 << QKeySequence::Undo
5145                 // overwriting '1234' with '6'
5146                 << "6";
5147
5148         expectedString << "ab6cd";
5149         // for versions previous to 3.2 we overwrite needed two undo operations
5150         expectedString << "ab1234cd";
5151         expectedString << "abcd";
5152
5153         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5154     } {
5155         KeyList keys;
5156         QStringList expectedString;
5157
5158         // inserting 'ABC'
5159         keys << "ABC"
5160                 // removes 'C'
5161                 << Qt::Key_Backspace;
5162
5163         expectedString << "AB";
5164         expectedString << "ABC";
5165
5166         QTest::newRow("Inserts,backspace") << keys << expectedString;
5167     } {
5168         KeyList keys;
5169         QStringList expectedString;
5170
5171         keys << "ABC"
5172                 // removes 'C'
5173                 << Qt::Key_Backspace
5174                 // inserting 'Z'
5175                 << "Z";
5176
5177         expectedString << "ABZ";
5178         expectedString << "AB";
5179         expectedString << "ABC";
5180
5181         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5182     } {
5183         KeyList keys;
5184         QStringList expectedString;
5185
5186         // inserting '123'
5187         keys << "123" << QKeySequence::MoveToStartOfLine
5188             // selecting '123'
5189              << QKeySequence::SelectEndOfLine
5190             // overwriting '123' with 'ABC'
5191              << "ABC";
5192
5193         expectedString << "ABC";
5194         // for versions previous to 3.2 we overwrite needed two undo operations
5195         expectedString << "123";
5196
5197         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5198     } {
5199         KeyList keys;
5200         QStringList expectedString;
5201
5202         // inserting '123'
5203         keys << "123"
5204                 << QKeySequence::Undo
5205                 << QKeySequence::Redo;
5206
5207         expectedString << "123";
5208         expectedString << QString();
5209
5210         QTest::newRow("Insert,undo,redo") << keys << expectedString;
5211     }
5212 }
5213
5214 void tst_qquicktextinput::undo_keypressevents()
5215 {
5216     QFETCH(KeyList, keys);
5217     QFETCH(QStringList, expectedString);
5218
5219     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5220     QQmlComponent textInputComponent(&engine);
5221     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5222     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5223     QVERIFY(textInput != 0);
5224
5225     QQuickCanvas canvas;
5226     textInput->setParentItem(canvas.rootItem());
5227     canvas.show();
5228     canvas.requestActivateWindow();
5229     QTest::qWaitForWindowShown(&canvas);
5230     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5231
5232     simulateKeys(&canvas, keys);
5233
5234     for (int i = 0; i < expectedString.size(); ++i) {
5235         QCOMPARE(textInput->text() , expectedString[i]);
5236         textInput->undo();
5237     }
5238     QVERIFY(textInput->text().isEmpty());
5239 }
5240
5241 void tst_qquicktextinput::QTBUG_19956()
5242 {
5243     QFETCH(QString, url);
5244
5245     QQuickView canvas(testFileUrl(url));
5246     canvas.show();
5247     canvas.requestActivateWindow();
5248     QTest::qWaitForWindowShown(&canvas);
5249     QVERIFY(canvas.rootObject() != 0);
5250     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5251     QVERIFY(input);
5252     input->setFocus(true);
5253     QVERIFY(input->hasActiveFocus());
5254
5255     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
5256     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5257     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
5258     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5259
5260     canvas.rootObject()->setProperty("topvalue", 15);
5261     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
5262     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5263
5264     canvas.rootObject()->setProperty("topvalue", 25);
5265     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
5266     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5267
5268     canvas.rootObject()->setProperty("bottomvalue", 21);
5269     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
5270     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5271
5272     canvas.rootObject()->setProperty("bottomvalue", 10);
5273     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5274     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5275 }
5276
5277 void tst_qquicktextinput::QTBUG_19956_regexp()
5278 {
5279     QUrl url = testFileUrl("qtbug-19956regexp.qml");
5280
5281     QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5282     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5283
5284     QQuickView canvas(url);
5285     canvas.show();
5286     canvas.requestActivateWindow();
5287     QTest::qWaitForWindowShown(&canvas);
5288     QVERIFY(canvas.rootObject() != 0);
5289     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5290     QVERIFY(input);
5291     input->setFocus(true);
5292     QVERIFY(input->hasActiveFocus());
5293
5294     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5295     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5296     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
5297     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5298
5299     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5300     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5301     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5302
5303     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5304     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5305     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5306 }
5307
5308 void tst_qquicktextinput::implicitSize_data()
5309 {
5310     QTest::addColumn<QString>("text");
5311     QTest::addColumn<QString>("wrap");
5312     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5313     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5314 }
5315
5316 void tst_qquicktextinput::implicitSize()
5317 {
5318     QFETCH(QString, text);
5319     QFETCH(QString, wrap);
5320     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5321     QQmlComponent textComponent(&engine);
5322     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5323     QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5324
5325     QVERIFY(textObject->width() < textObject->implicitWidth());
5326     QVERIFY(textObject->height() == textObject->implicitHeight());
5327
5328     textObject->resetWidth();
5329     QVERIFY(textObject->width() == textObject->implicitWidth());
5330     QVERIFY(textObject->height() == textObject->implicitHeight());
5331 }
5332
5333 void tst_qquicktextinput::implicitSizeBinding_data()
5334 {
5335     implicitSize_data();
5336 }
5337
5338 void tst_qquicktextinput::implicitSizeBinding()
5339 {
5340     QFETCH(QString, text);
5341     QFETCH(QString, wrap);
5342     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5343     QQmlComponent textComponent(&engine);
5344     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5345     QScopedPointer<QObject> object(textComponent.create());
5346     QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5347
5348     QCOMPARE(textObject->width(), textObject->implicitWidth());
5349     QCOMPARE(textObject->height(), textObject->implicitHeight());
5350
5351     textObject->resetWidth();
5352     QCOMPARE(textObject->width(), textObject->implicitWidth());
5353     QCOMPARE(textObject->height(), textObject->implicitHeight());
5354
5355     textObject->resetHeight();
5356     QCOMPARE(textObject->width(), textObject->implicitWidth());
5357     QCOMPARE(textObject->height(), textObject->implicitHeight());
5358 }
5359
5360
5361 void tst_qquicktextinput::negativeDimensions()
5362 {
5363     // Verify this doesn't assert during initialization.
5364     QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5365     QScopedPointer<QObject> o(component.create());
5366     QVERIFY(o);
5367     QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5368     QVERIFY(input);
5369     QCOMPARE(input->width(), qreal(-1));
5370     QCOMPARE(input->height(), qreal(-1));
5371 }
5372
5373
5374 void tst_qquicktextinput::setInputMask_data()
5375 {
5376     QTest::addColumn<QString>("mask");
5377     QTest::addColumn<QString>("input");
5378     QTest::addColumn<QString>("expectedText");
5379     QTest::addColumn<QString>("expectedDisplay");
5380     QTest::addColumn<bool>("insert_text");
5381
5382     // both keyboard and insert()
5383     for (int i=0; i<2; i++) {
5384         bool insert_text = i==0 ? false : true;
5385         QString insert_mode = "keys ";
5386         if (insert_text)
5387             insert_mode = "insert ";
5388
5389         QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5390             << QString("000.000.000.000")
5391             << QString("127.0.0.1")
5392             << QString("127.0.0.1")
5393             << QString("127.0  .0  .1  ")
5394             << bool(insert_text);
5395         QTest::newRow(QString(insert_mode + "mac").toLatin1())
5396             << QString("HH:HH:HH:HH:HH:HH;#")
5397             << QString("00:E0:81:21:9E:8E")
5398             << QString("00:E0:81:21:9E:8E")
5399             << QString("00:E0:81:21:9E:8E")
5400             << bool(insert_text);
5401         QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5402             << QString("<HH:>HH:!HH:HH:HH:HH;#")
5403             << QString("AAe081219E8E")
5404             << QString("aa:E0:81:21:9E:8E")
5405             << QString("aa:E0:81:21:9E:8E")
5406             << bool(insert_text);
5407         QTest::newRow(QString(insert_mode + "byte").toLatin1())
5408             << QString("BBBBBBBB;0")
5409             << QString("11011001")
5410             << QString("11111")
5411             << QString("11011001")
5412             << bool(insert_text);
5413         QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5414             << QString("bbbb.bbbb;-")
5415             << QString("110. 0001")
5416             << QString("110.0001")
5417             << QString("110-.0001")
5418             << bool(insert_text);
5419         QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5420             << QString("000.000.000.000;0")
5421             << QString("127.0.0.1")
5422             << QString("127...1")
5423             << QString("127.000.000.100")
5424             << bool(insert_text);
5425         QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5426             << QString("000.000.000.000")
5427             << QString(".0.0.1")
5428             << QString(".0.0.1")
5429             << QString("   .0  .0  .1  ")
5430             << bool(insert_text);
5431         QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5432             << QString("000.000.000.000")
5433             << QString()
5434             << QString("...")
5435             << QString("   .   .   .   ")
5436             << bool(insert_text);
5437         QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5438             << QString("000.000.000.000;#")
5439             << QString()
5440             << QString("...")
5441             << QString("###.###.###.###")
5442             << bool(insert_text);
5443         QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5444             << QString("000.000.000.000")
5445             << QString("1234123412341234")
5446             << QString("123.412.341.234")
5447             << QString("123.412.341.234")
5448             << bool(insert_text);
5449         QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5450             << QString(">AAAA")
5451             << QString("AbCd")
5452             << QString("ABCD")
5453             << QString("ABCD")
5454             << bool(insert_text);
5455         QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5456             << QString("<AAAA")
5457             << QString("AbCd")
5458             << QString("abcd")
5459             << QString("abcd")
5460             << bool(insert_text);
5461
5462         QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5463             << QString("!AAAA")
5464             << QString("AbCd")
5465             << QString("AbCd")
5466             << QString("AbCd")
5467             << bool(insert_text);
5468         QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5469             << QString("!A!A!A!A")
5470             << QString("AbCd")
5471             << QString("AbCd")
5472             << QString("AbCd")
5473             << bool(insert_text);
5474         QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5475             << QString("AAAA")
5476             << QString("AbCd")
5477             << QString("AbCd")
5478             << QString("AbCd")
5479             << bool(insert_text);
5480
5481         QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5482             << QString("{n}[0]")
5483             << QString("A9")
5484             << QString("A9")
5485             << QString("A9")
5486             << bool(insert_text);
5487         QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5488             << QString("\\\\N\\\\n00")
5489             << QString("9")
5490             << QString("Nn9")
5491             << QString("Nn9 ")
5492             << bool(insert_text);
5493         QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5494             << QString("\\\\\\\\00")
5495             << QString("0")
5496             << QString("\\0")
5497             << QString("\\0 ")
5498             << bool(insert_text);
5499         QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5500             << QString("\\\\(00\\\\)")
5501             << QString("0")
5502             << QString("(0)")
5503             << QString("(0 )")
5504             << bool(insert_text);
5505
5506         QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5507             << QString(">AAAA<AAAA!AAAA")
5508             << QString("AbCdEfGhIjKl")
5509             << QString("ABCDefghIjKl")
5510             << QString("ABCDefghIjKl")
5511             << bool(insert_text);
5512         QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5513             << QString(">aaaa<aaaa!aaaa")
5514             << QString("AbCdEfGhIjKl")
5515             << QString("ABCDefghIjKl")
5516             << QString("ABCDefghIjKl")
5517             << bool(insert_text);
5518
5519         QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5520             << QString(">A<A<A>A>A<A!A!A")
5521             << QString("AbCdEFGH")
5522             << QString("AbcDEfGH")
5523             << QString("AbcDEfGH")
5524             << bool(insert_text);
5525         QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5526             << QString(">A<A<A>A>A<A!A!A")
5527             << QString("aBcDefgh")
5528             << QString("AbcDEfgh")
5529             << QString("AbcDEfgh")
5530             << bool(insert_text);
5531         QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5532             << QString(">a<a<a>a>a<a!a!a")
5533             << QString("AbCdEFGH")
5534             << QString("AbcDEfGH")
5535             << QString("AbcDEfGH")
5536             << bool(insert_text);
5537         QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5538             << QString(">a<a<a>a>a<a!a!a")
5539             << QString("aBcDefgh")
5540             << QString("AbcDEfgh")
5541             << QString("AbcDEfgh")
5542             << bool(insert_text);
5543         QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5544             << QString(">H<H<H>H>H<H!H!H")
5545             << QString("aBcDef01")
5546             << QString("AbcDEf01")
5547             << QString("AbcDEf01")
5548             << bool(insert_text);
5549         QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5550             << QString(">h<h<h>h>h<h!h!h")
5551             << QString("aBcDef92")
5552             << QString("AbcDEf92")
5553             << QString("AbcDEf92")
5554             << bool(insert_text);
5555
5556         QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5557             << QString("AAAAAAAA")
5558             << QString("A2#a;.0!")
5559             << QString("Aa")
5560             << QString("Aa      ")
5561             << bool(insert_text);
5562         QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5563             << QString("AAAA")
5564             << QString("f4f4f4f4")
5565             << QString("ffff")
5566             << QString("ffff")
5567             << bool(insert_text);
5568         QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5569             << QString("9999;0")
5570             << QString("2004")
5571             << QString("2004")
5572             << QString("2004")
5573             << bool(insert_text);
5574     }
5575 }
5576
5577 void tst_qquicktextinput::setInputMask()
5578 {
5579     QFETCH(QString, mask);
5580     QFETCH(QString, input);
5581     QFETCH(QString, expectedText);
5582     QFETCH(QString, expectedDisplay);
5583     QFETCH(bool, insert_text);
5584
5585     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5586     QQmlComponent textInputComponent(&engine);
5587     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5588     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5589     QVERIFY(textInput != 0);
5590
5591     // then either insert using insert() or keyboard
5592     if (insert_text) {
5593         textInput->insert(0, input);
5594     } else {
5595         QQuickCanvas canvas;
5596         textInput->setParentItem(canvas.rootItem());
5597         canvas.show();
5598         canvas.requestActivateWindow();
5599         QTest::qWaitForWindowShown(&canvas);
5600         QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5601
5602         simulateKey(&canvas, Qt::Key_Home);
5603         for (int i = 0; i < input.length(); i++)
5604             QTest::keyClick(&canvas, input.at(i).toLatin1());
5605     }
5606
5607     QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5608     QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5609
5610     QCOMPARE(textInput->text(), expectedText);
5611     QCOMPARE(textInput->displayText(), expectedDisplay);
5612 }
5613
5614 void tst_qquicktextinput::inputMask_data()
5615 {
5616     QTest::addColumn<QString>("mask");
5617     QTest::addColumn<QString>("expectedMask");
5618
5619     // if no mask is set a nul string should be returned
5620     QTest::newRow("nul 1") << QString("") << QString();
5621     QTest::newRow("nul 2") << QString() << QString();
5622
5623     // try different masks
5624     QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5625     QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5626     QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5627     QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5628
5629     // set an invalid input mask...
5630     // the current behaviour is that this exact (faulty) string is returned.
5631     QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5632
5633     // verify that we can unset the mask again
5634     QTest::newRow("unset") << QString("") << QString();
5635 }
5636
5637 void tst_qquicktextinput::inputMask()
5638 {
5639     QFETCH(QString, mask);
5640     QFETCH(QString, expectedMask);
5641
5642     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5643     QQmlComponent textInputComponent(&engine);
5644     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5645     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5646     QVERIFY(textInput != 0);
5647
5648     QCOMPARE(textInput->inputMask(), expectedMask);
5649 }
5650
5651 void tst_qquicktextinput::clearInputMask()
5652 {
5653     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
5654     QQmlComponent textInputComponent(&engine);
5655     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5656     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5657     QVERIFY(textInput != 0);
5658
5659     QVERIFY(textInput->inputMask() != QString());
5660     textInput->setInputMask(QString());
5661     QCOMPARE(textInput->inputMask(), QString());
5662 }
5663
5664 void tst_qquicktextinput::keypress_inputMask_data()
5665 {
5666     QTest::addColumn<QString>("mask");
5667     QTest::addColumn<KeyList>("keys");
5668     QTest::addColumn<QString>("expectedText");
5669     QTest::addColumn<QString>("expectedDisplayText");
5670
5671     {
5672         KeyList keys;
5673         // inserting 'A1.2B'
5674         keys << Qt::Key_Home << "A1.2B";
5675         QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
5676     }
5677     {
5678         KeyList keys;
5679         // inserting '0!P3'
5680         keys << Qt::Key_Home << "0!P3";
5681         QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
5682     }
5683     {
5684         KeyList keys;
5685         // pressing delete
5686         keys << Qt::Key_Home
5687              << Qt::Key_Delete;
5688         QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5689     }
5690     {
5691         KeyList keys;
5692         // selecting all and delete
5693         keys << Qt::Key_Home
5694              << Key(Qt::ShiftModifier, Qt::Key_End)
5695              << Qt::Key_Delete;
5696         QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5697     }
5698     {
5699         KeyList keys;
5700         // inserting '12.12' then two backspaces
5701         keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
5702         QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
5703     }
5704     {
5705         KeyList keys;
5706         // inserting '12ab'
5707         keys << Qt::Key_Home << "12ab";
5708         QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
5709     }
5710 }
5711
5712 void tst_qquicktextinput::keypress_inputMask()
5713 {
5714     QFETCH(QString, mask);
5715     QFETCH(KeyList, keys);
5716     QFETCH(QString, expectedText);
5717     QFETCH(QString, expectedDisplayText);
5718
5719     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5720     QQmlComponent textInputComponent(&engine);
5721     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5722     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5723     QVERIFY(textInput != 0);
5724
5725     QQuickCanvas canvas;
5726     textInput->setParentItem(canvas.rootItem());
5727     canvas.show();
5728     canvas.requestActivateWindow();
5729     QTest::qWaitForWindowShown(&canvas);
5730     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5731
5732     simulateKeys(&canvas, keys);
5733
5734     QCOMPARE(textInput->text(), expectedText);
5735     QCOMPARE(textInput->displayText(), expectedDisplayText);
5736 }
5737
5738
5739 void tst_qquicktextinput::hasAcceptableInputMask_data()
5740 {
5741     QTest::addColumn<QString>("optionalMask");
5742     QTest::addColumn<QString>("requiredMask");
5743     QTest::addColumn<QString>("invalid");
5744     QTest::addColumn<QString>("valid");
5745
5746     QTest::newRow("Alphabetic optional and required")
5747         << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
5748     QTest::newRow("Alphanumeric optional and require")
5749         << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
5750     QTest::newRow("Any optional and required")
5751         << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
5752     QTest::newRow("Numeric (0-9) required")
5753         << QString("0000") << QString("9999") << QString("11") << QString("1138");
5754     QTest::newRow("Numeric (1-9) optional and required")
5755         << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
5756 }
5757
5758 void tst_qquicktextinput::hasAcceptableInputMask()
5759 {
5760     QFETCH(QString, optionalMask);
5761     QFETCH(QString, requiredMask);
5762     QFETCH(QString, invalid);
5763     QFETCH(QString, valid);
5764
5765     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + optionalMask + "\" }";
5766     QQmlComponent textInputComponent(&engine);
5767     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5768     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5769     QVERIFY(textInput != 0);
5770
5771     QQuickCanvas canvas;
5772     textInput->setParentItem(canvas.rootItem());
5773     canvas.show();
5774     canvas.requestActivateWindow();
5775     QTest::qWaitForWindowShown(&canvas);
5776     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5777
5778     // test that invalid input (for required) work for optionalMask
5779     textInput->setText(invalid);
5780     QVERIFY(textInput->hasAcceptableInput());
5781
5782     // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
5783     QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
5784
5785     // test requiredMask
5786     textInput->setInputMask(requiredMask);
5787     textInput->setText(invalid);
5788     QVERIFY(!textInput->hasAcceptableInput());
5789
5790     textInput->setText(valid);
5791     QVERIFY(textInput->hasAcceptableInput());
5792 }
5793
5794 void tst_qquicktextinput::maskCharacter_data()
5795 {
5796     QTest::addColumn<QString>("mask");
5797     QTest::addColumn<QString>("input");
5798     QTest::addColumn<bool>("expectedValid");
5799
5800     QTest::newRow("Hex") << QString("H")
5801                          << QString("0123456789abcdefABCDEF") << true;
5802     QTest::newRow("hex") << QString("h")
5803                          << QString("0123456789abcdefABCDEF") << true;
5804     QTest::newRow("HexInvalid") << QString("H")
5805                                 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5806                                 << false;
5807     QTest::newRow("hexInvalid") << QString("h")
5808                                 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5809                                 << false;
5810     QTest::newRow("Bin") << QString("B")
5811                          << QString("01") << true;
5812     QTest::newRow("bin") << QString("b")
5813                          << QString("01") << true;
5814     QTest::newRow("BinInvalid") << QString("B")
5815                                 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5816                                 << false;
5817     QTest::newRow("binInvalid") << QString("b")
5818                                 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5819                                 << false;
5820 }
5821
5822 void tst_qquicktextinput::maskCharacter()
5823 {
5824     QFETCH(QString, mask);
5825     QFETCH(QString, input);
5826     QFETCH(bool, expectedValid);
5827
5828     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5829     QQmlComponent textInputComponent(&engine);
5830     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5831     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5832     QVERIFY(textInput != 0);
5833
5834     QQuickCanvas canvas;
5835     textInput->setParentItem(canvas.rootItem());
5836     canvas.show();
5837     canvas.requestActivateWindow();
5838     QTest::qWaitForWindowShown(&canvas);
5839     QTRY_COMPARE(QGuiApplication::focusWindow(), &canvas);
5840
5841     for (int i = 0; i < input.size(); ++i) {
5842         QString in = QString(input.at(i));
5843         QString expected = expectedValid ? in : QString();
5844         textInput->setText(QString(input.at(i)));
5845         QCOMPARE(textInput->text(), expected);
5846     }
5847 }
5848
5849 QTEST_MAIN(tst_qquicktextinput)
5850
5851 #include "tst_qquicktextinput.moc"