5397b6ce9f9c01898557086450a926b220132a88
[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
1446 void tst_qquicktextinput::verticalAlignment()
1447 {
1448     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1449     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1450     QVERIFY(textInput != 0);
1451     canvas.show();
1452
1453     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1454     QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1455     QVERIFY(textInput->cursorRectangle().bottom() < canvas.height() / 2);
1456     QVERIFY(textInput->positionToRectangle(0).bottom() < canvas.height() / 2);
1457
1458     // bottom aligned
1459     textInput->setVAlign(QQuickTextInput::AlignBottom);
1460     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1461     QVERIFY(textInput->boundingRect().top() > canvas.height() / 2);
1462     QVERIFY(textInput->cursorRectangle().top() > canvas.height() / 2);
1463     QVERIFY(textInput->positionToRectangle(0).top() > canvas.height() / 2);
1464
1465     // explicitly center aligned
1466     textInput->setVAlign(QQuickTextInput::AlignVCenter);
1467     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1468     QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1469     QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1470     QVERIFY(textInput->cursorRectangle().top() < canvas.height() / 2);
1471     QVERIFY(textInput->cursorRectangle().bottom() > canvas.height() / 2);
1472     QVERIFY(textInput->positionToRectangle(0).top() < canvas.height() / 2);
1473     QVERIFY(textInput->positionToRectangle(0).bottom() > canvas.height() / 2);
1474 }
1475
1476 void tst_qquicktextinput::clipRect()
1477 {
1478     QQmlComponent component(&engine);
1479     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1480     QScopedPointer<QObject> object(component.create());
1481     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1482     QVERIFY(input);
1483
1484     QCOMPARE(input->clipRect().x(), qreal(0));
1485     QCOMPARE(input->clipRect().y(), qreal(0));
1486     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1487     QCOMPARE(input->clipRect().height(), input->height());
1488
1489     input->setText("Hello World");
1490     QCOMPARE(input->clipRect().x(), qreal(0));
1491     QCOMPARE(input->clipRect().y(), qreal(0));
1492     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1493     QCOMPARE(input->clipRect().height(), input->height());
1494
1495     // clip rect shouldn't exceed the size of the item, expect for the cursor width;
1496     input->setWidth(input->width() / 2);
1497     QCOMPARE(input->clipRect().x(), qreal(0));
1498     QCOMPARE(input->clipRect().y(), qreal(0));
1499     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1500     QCOMPARE(input->clipRect().height(), input->height());
1501
1502     input->setHeight(input->height() * 2);
1503     QCOMPARE(input->clipRect().x(), qreal(0));
1504     QCOMPARE(input->clipRect().y(), qreal(0));
1505     QCOMPARE(input->clipRect().width(), input->width() + input->cursorRectangle().width());
1506     QCOMPARE(input->clipRect().height(), input->height());
1507
1508     QQmlComponent cursorComponent(&engine);
1509     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1510
1511     input->setCursorDelegate(&cursorComponent);
1512     input->setCursorVisible(true);
1513
1514     // If a cursor delegate is used it's size should determine the excess width.
1515     QCOMPARE(input->clipRect().x(), qreal(0));
1516     QCOMPARE(input->clipRect().y(), qreal(0));
1517     QCOMPARE(input->clipRect().width(), input->width() + 8);
1518     QCOMPARE(input->clipRect().height(), input->height());
1519
1520     // Alignment, auto scroll, wrapping all don't affect the clip rect.
1521     input->setAutoScroll(false);
1522     QCOMPARE(input->clipRect().x(), qreal(0));
1523     QCOMPARE(input->clipRect().y(), qreal(0));
1524     QCOMPARE(input->clipRect().width(), input->width() + 8);
1525     QCOMPARE(input->clipRect().height(), input->height());
1526
1527     input->setHAlign(QQuickTextInput::AlignRight);
1528     QCOMPARE(input->clipRect().x(), qreal(0));
1529     QCOMPARE(input->clipRect().y(), qreal(0));
1530     QCOMPARE(input->clipRect().width(), input->width() + 8);
1531     QCOMPARE(input->clipRect().height(), input->height());
1532
1533     input->setWrapMode(QQuickTextInput::Wrap);
1534     QCOMPARE(input->clipRect().x(), qreal(0));
1535     QCOMPARE(input->clipRect().y(), qreal(0));
1536     QCOMPARE(input->clipRect().width(), input->width() + 8);
1537     QCOMPARE(input->clipRect().height(), input->height());
1538
1539     input->setVAlign(QQuickTextInput::AlignBottom);
1540     QCOMPARE(input->clipRect().x(), qreal(0));
1541     QCOMPARE(input->clipRect().y(), qreal(0));
1542     QCOMPARE(input->clipRect().width(), input->width() + 8);
1543     QCOMPARE(input->clipRect().height(), input->height());
1544 }
1545
1546 void tst_qquicktextinput::boundingRect()
1547 {
1548     QQmlComponent component(&engine);
1549     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1550     QScopedPointer<QObject> object(component.create());
1551     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1552     QVERIFY(input);
1553
1554     QTextLayout layout;
1555     layout.setFont(input->font());
1556
1557     if (!qmlDisableDistanceField()) {
1558         QTextOption option;
1559         option.setUseDesignMetrics(true);
1560         layout.setTextOption(option);
1561     }
1562     layout.beginLayout();
1563     QTextLine line = layout.createLine();
1564     layout.endLayout();
1565
1566     QCOMPARE(input->boundingRect().x(), qreal(0));
1567     QCOMPARE(input->boundingRect().y(), qreal(0));
1568     QCOMPARE(input->boundingRect().width(), input->cursorRectangle().width());
1569     QCOMPARE(input->boundingRect().height(), line.height());
1570
1571     input->setText("Hello World");
1572
1573     layout.setText(input->text());
1574     layout.beginLayout();
1575     line = layout.createLine();
1576     layout.endLayout();
1577
1578     QCOMPARE(input->boundingRect().x(), qreal(0));
1579     QCOMPARE(input->boundingRect().y(), qreal(0));
1580     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1581     QCOMPARE(input->boundingRect().height(), line.height());
1582
1583     // the size of the bounding rect shouldn't be bounded by the size of item.
1584     input->setWidth(input->width() / 2);
1585     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1586     QCOMPARE(input->boundingRect().y(), qreal(0));
1587     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1588     QCOMPARE(input->boundingRect().height(), line.height());
1589
1590     input->setHeight(input->height() * 2);
1591     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1592     QCOMPARE(input->boundingRect().y(), qreal(0));
1593     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width());
1594     QCOMPARE(input->boundingRect().height(), line.height());
1595
1596     QQmlComponent cursorComponent(&engine);
1597     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1598
1599     input->setCursorDelegate(&cursorComponent);
1600     input->setCursorVisible(true);
1601
1602     // Don't include the size of a cursor delegate as it has its own bounding rect.
1603     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1604     QCOMPARE(input->boundingRect().y(), qreal(0));
1605     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1606     QCOMPARE(input->boundingRect().height(), line.height());
1607
1608     // Bounding rect left aligned when auto scroll is disabled;
1609     input->setAutoScroll(false);
1610     QCOMPARE(input->boundingRect().x(), qreal(0));
1611     QCOMPARE(input->boundingRect().y(), qreal(0));
1612     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1613     QCOMPARE(input->boundingRect().height(), line.height());
1614
1615     input->setHAlign(QQuickTextInput::AlignRight);
1616     QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth());
1617     QCOMPARE(input->boundingRect().y(), qreal(0));
1618     QCOMPARE(input->boundingRect().width(), line.naturalTextWidth());
1619     QCOMPARE(input->boundingRect().height(), line.height());
1620
1621     input->setWrapMode(QQuickTextInput::Wrap);
1622     QCOMPARE(input->boundingRect().right(), input->width());
1623     QCOMPARE(input->boundingRect().y(), qreal(0));
1624     QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1625     QVERIFY(input->boundingRect().height() > line.height());
1626
1627     input->setVAlign(QQuickTextInput::AlignBottom);
1628     QCOMPARE(input->boundingRect().right(), input->width());
1629     QCOMPARE(input->boundingRect().bottom(), input->height());
1630     QVERIFY(input->boundingRect().width() < line.naturalTextWidth());
1631     QVERIFY(input->boundingRect().height() > line.height());
1632 }
1633
1634 void tst_qquicktextinput::positionAt()
1635 {
1636     QQuickView canvas(testFileUrl("positionAt.qml"));
1637     QVERIFY(canvas.rootObject() != 0);
1638     canvas.show();
1639     canvas.requestActivateWindow();
1640     QTest::qWaitForWindowShown(&canvas);
1641
1642     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1643     QVERIFY(textinputObject != 0);
1644
1645     // Check autoscrolled...
1646
1647     int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1648
1649     QTextLayout layout(textinputObject->text());
1650     layout.setFont(textinputObject->font());
1651
1652     if (!qmlDisableDistanceField()) {
1653         QTextOption option;
1654         option.setUseDesignMetrics(true);
1655         layout.setTextOption(option);
1656     }
1657     layout.beginLayout();
1658     QTextLine line = layout.createLine();
1659     layout.endLayout();
1660
1661     int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1662     int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1663     int textWidth = floor(line.horizontalAdvance());
1664
1665     QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1666     QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1667
1668     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1669     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1670     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1671
1672     // Check without autoscroll...
1673     textinputObject->setAutoScroll(false);
1674     pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1675
1676     textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1677     textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1678
1679     QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1680     QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1681
1682     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1683     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1684     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1685
1686     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1687     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1688
1689     QString preeditText = textinputObject->text().mid(0, pos);
1690     textinputObject->setText(textinputObject->text().mid(pos));
1691     textinputObject->setCursorPosition(0);
1692
1693     {   QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1694         QVERIFY(qGuiApp->focusObject());
1695         QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1696
1697     // Check all points within the preedit text return the same position.
1698     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1699     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1700     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1701
1702     // Verify positioning returns to normal after the preedit text.
1703     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1704     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1705
1706     {   QInputMethodEvent inputEvent;
1707         QVERIFY(qGuiApp->focusObject());
1708         QGuiApplication::sendEvent(qGuiApp->focusObject(), &inputEvent); }
1709
1710     // With wrapping.
1711     textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1712
1713     const qreal y0 = line.height() / 2;
1714     const qreal y1 = line.height() * 3 / 2;
1715
1716     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1717     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1718
1719     int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1720     QVERIFY(newLinePos > pos);
1721     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1722 }
1723
1724 void tst_qquicktextinput::maxLength()
1725 {
1726     QQuickView canvas(testFileUrl("maxLength.qml"));
1727     QVERIFY(canvas.rootObject() != 0);
1728     canvas.show();
1729     canvas.requestActivateWindow();
1730     QTest::qWaitForWindowShown(&canvas);
1731
1732     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1733     QVERIFY(textinputObject != 0);
1734     QVERIFY(textinputObject->text().isEmpty());
1735     QVERIFY(textinputObject->maxLength() == 10);
1736     foreach (const QString &str, standard) {
1737         QVERIFY(textinputObject->text().length() <= 10);
1738         textinputObject->setText(str);
1739         QVERIFY(textinputObject->text().length() <= 10);
1740     }
1741
1742     textinputObject->setText("");
1743     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1744     for (int i=0; i<20; i++) {
1745         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1746         //simulateKey(&canvas, Qt::Key_A);
1747         QTest::keyPress(&canvas, Qt::Key_A);
1748         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1749         QTest::qWait(50);
1750     }
1751 }
1752
1753 void tst_qquicktextinput::masks()
1754 {
1755     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1756     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1757     QQuickView canvas(testFileUrl("masks.qml"));
1758     canvas.show();
1759     canvas.requestActivateWindow();
1760     QVERIFY(canvas.rootObject() != 0);
1761     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1762     QVERIFY(textinputObject != 0);
1763     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1764     QVERIFY(textinputObject->text().length() == 0);
1765     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1766     QCOMPARE(textinputObject->length(), 8);
1767     for (int i=0; i<10; i++) {
1768         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1769         QCOMPARE(textinputObject->length(), 8);
1770         QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1771         QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1772         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1773         //simulateKey(&canvas, Qt::Key_A);
1774         QTest::keyPress(&canvas, Qt::Key_A);
1775         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1776         QTest::qWait(50);
1777     }
1778 }
1779
1780 void tst_qquicktextinput::validators()
1781 {
1782     // Note that this test assumes that the validators are working properly
1783     // so you may need to run their tests first. All validators are checked
1784     // here to ensure that their exposure to QML is working.
1785
1786     QLocale::setDefault(QLocale(QStringLiteral("C")));
1787
1788     QQuickView canvas(testFileUrl("validators.qml"));
1789     canvas.show();
1790     canvas.requestActivateWindow();
1791
1792     QVERIFY(canvas.rootObject() != 0);
1793
1794     QLocale defaultLocale;
1795     QLocale enLocale("en");
1796     QLocale deLocale("de_DE");
1797
1798     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1799     QVERIFY(intInput);
1800     QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1801
1802     QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1803     QVERIFY(intValidator);
1804     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1805     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1806     intValidator->setLocaleName(enLocale.name());
1807     QCOMPARE(intValidator->localeName(), enLocale.name());
1808     QCOMPARE(intInput->validator()->locale(), enLocale);
1809     intValidator->resetLocaleName();
1810     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1811     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1812
1813     intInput->setFocus(true);
1814     QTRY_VERIFY(intInput->hasActiveFocus());
1815     QCOMPARE(intInput->hasAcceptableInput(), false);
1816     QCOMPARE(intInput->property("acceptable").toBool(), false);
1817     QTest::keyPress(&canvas, Qt::Key_1);
1818     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1819     QTest::qWait(50);
1820     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1821     QCOMPARE(intInput->hasAcceptableInput(), false);
1822     QCOMPARE(intInput->property("acceptable").toBool(), false);
1823     QCOMPARE(intSpy.count(), 0);
1824     QTest::keyPress(&canvas, Qt::Key_2);
1825     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1826     QTest::qWait(50);
1827     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1828     QCOMPARE(intInput->hasAcceptableInput(), false);
1829     QCOMPARE(intInput->property("acceptable").toBool(), false);
1830     QCOMPARE(intSpy.count(), 0);
1831     QTest::keyPress(&canvas, Qt::Key_Period);
1832     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1833     QTest::qWait(50);
1834     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1835     QCOMPARE(intInput->hasAcceptableInput(), false);
1836     QTest::keyPress(&canvas, Qt::Key_Comma);
1837     QTest::keyRelease(&canvas, Qt::Key_Comma, 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_Backspace);
1842     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1843     QTest::qWait(50);
1844     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1845     QCOMPARE(intInput->hasAcceptableInput(), false);
1846     intValidator->setLocaleName(deLocale.name());
1847     QTest::keyPress(&canvas, Qt::Key_Period);
1848     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1849     QTest::qWait(50);
1850     QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1851     QCOMPARE(intInput->hasAcceptableInput(), false);
1852     QTest::keyPress(&canvas, Qt::Key_Backspace);
1853     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1854     QTest::qWait(50);
1855     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1856     QCOMPARE(intInput->hasAcceptableInput(), false);
1857     intValidator->resetLocaleName();
1858     QTest::keyPress(&canvas, Qt::Key_1);
1859     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1860     QTest::qWait(50);
1861     QCOMPARE(intInput->text(), QLatin1String("11"));
1862     QCOMPARE(intInput->hasAcceptableInput(), true);
1863     QCOMPARE(intInput->property("acceptable").toBool(), true);
1864     QCOMPARE(intSpy.count(), 1);
1865     QTest::keyPress(&canvas, Qt::Key_0);
1866     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1867     QTest::qWait(50);
1868     QCOMPARE(intInput->text(), QLatin1String("11"));
1869     QCOMPARE(intInput->hasAcceptableInput(), true);
1870     QCOMPARE(intInput->property("acceptable").toBool(), true);
1871     QCOMPARE(intSpy.count(), 1);
1872
1873     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1874     QVERIFY(dblInput);
1875     QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1876
1877     QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1878     QVERIFY(dblValidator);
1879     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1880     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1881     dblValidator->setLocaleName(enLocale.name());
1882     QCOMPARE(dblValidator->localeName(), enLocale.name());
1883     QCOMPARE(dblInput->validator()->locale(), enLocale);
1884     dblValidator->resetLocaleName();
1885     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1886     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1887
1888     dblInput->setFocus(true);
1889     QVERIFY(dblInput->hasActiveFocus() == true);
1890     QCOMPARE(dblInput->hasAcceptableInput(), false);
1891     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1892     QTest::keyPress(&canvas, Qt::Key_1);
1893     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1894     QTest::qWait(50);
1895     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1896     QCOMPARE(dblInput->hasAcceptableInput(), false);
1897     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1898     QCOMPARE(dblSpy.count(), 0);
1899     QTest::keyPress(&canvas, Qt::Key_2);
1900     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1901     QTest::qWait(50);
1902     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1903     QCOMPARE(dblInput->hasAcceptableInput(), true);
1904     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1905     QCOMPARE(dblSpy.count(), 1);
1906     QTest::keyPress(&canvas, Qt::Key_Comma);
1907     QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1908     QTest::qWait(50);
1909     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1910     QCOMPARE(dblInput->hasAcceptableInput(), true);
1911     QTest::keyPress(&canvas, Qt::Key_1);
1912     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1913     QTest::qWait(50);
1914     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1915     QCOMPARE(dblInput->hasAcceptableInput(), true);
1916     dblValidator->setLocaleName(deLocale.name());
1917     QCOMPARE(dblInput->hasAcceptableInput(), true);
1918     QTest::keyPress(&canvas, Qt::Key_1);
1919     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1920     QTest::qWait(50);
1921     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
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,11"));
1927     QCOMPARE(dblInput->hasAcceptableInput(), true);
1928     QTest::keyPress(&canvas, Qt::Key_Backspace);
1929     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1930     QTest::qWait(50);
1931     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
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,"));
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     dblValidator->resetLocaleName();
1944     QTest::keyPress(&canvas, Qt::Key_Period);
1945     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1946     QTest::qWait(50);
1947     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1948     QCOMPARE(dblInput->hasAcceptableInput(), true);
1949     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1950     QCOMPARE(dblSpy.count(), 1);
1951     QTest::keyPress(&canvas, Qt::Key_1);
1952     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1953     QTest::qWait(50);
1954     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1955     QCOMPARE(dblInput->hasAcceptableInput(), true);
1956     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1957     QCOMPARE(dblSpy.count(), 1);
1958     QTest::keyPress(&canvas, Qt::Key_1);
1959     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1960     QTest::qWait(50);
1961     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1962     QCOMPARE(dblInput->hasAcceptableInput(), true);
1963     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1964     QCOMPARE(dblSpy.count(), 1);
1965     QTest::keyPress(&canvas, Qt::Key_1);
1966     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1967     QTest::qWait(50);
1968     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1969     QCOMPARE(dblInput->hasAcceptableInput(), true);
1970     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1971     QCOMPARE(dblSpy.count(), 1);
1972
1973     // Ensure the validator doesn't prevent characters being removed.
1974     dblInput->setValidator(intInput->validator());
1975     QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1976     QCOMPARE(dblInput->hasAcceptableInput(), false);
1977     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1978     QCOMPARE(dblSpy.count(), 2);
1979     QTest::keyPress(&canvas, Qt::Key_Backspace);
1980     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1981     QTest::qWait(50);
1982     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1983     QCOMPARE(dblInput->hasAcceptableInput(), false);
1984     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1985     QCOMPARE(dblSpy.count(), 2);
1986     // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1987     QTest::keyPress(&canvas, Qt::Key_1);
1988     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1989     QTest::qWait(50);
1990     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1991     QCOMPARE(dblInput->hasAcceptableInput(), false);
1992     QCOMPARE(dblSpy.count(), 2);
1993     QTest::keyPress(&canvas, Qt::Key_Backspace);
1994     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1995     QTest::qWait(50);
1996     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1997     QCOMPARE(dblInput->hasAcceptableInput(), false);
1998     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1999     QCOMPARE(dblSpy.count(), 2);
2000     QTest::keyPress(&canvas, Qt::Key_Backspace);
2001     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2002     QTest::qWait(50);
2003     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
2004     QCOMPARE(dblInput->hasAcceptableInput(), false);
2005     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2006     QCOMPARE(dblSpy.count(), 2);
2007     QTest::keyPress(&canvas, Qt::Key_Backspace);
2008     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2009     QTest::qWait(50);
2010     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
2011     QCOMPARE(dblInput->hasAcceptableInput(), false);
2012     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2013     QCOMPARE(dblSpy.count(), 2);
2014     QTest::keyPress(&canvas, Qt::Key_Backspace);
2015     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
2016     QTest::qWait(50);
2017     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
2018     QCOMPARE(dblInput->hasAcceptableInput(), false);
2019     QCOMPARE(dblInput->property("acceptable").toBool(), false);
2020     QCOMPARE(dblSpy.count(), 2);
2021     QTest::keyPress(&canvas, Qt::Key_1);
2022     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2023     QTest::qWait(50);
2024     QCOMPARE(dblInput->text(), QLatin1String("11"));
2025     QCOMPARE(dblInput->property("acceptable").toBool(), true);
2026     QCOMPARE(dblInput->hasAcceptableInput(), true);
2027     QCOMPARE(dblSpy.count(), 3);
2028
2029     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
2030     QVERIFY(strInput);
2031     QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
2032     strInput->setFocus(true);
2033     QVERIFY(strInput->hasActiveFocus() == true);
2034     QCOMPARE(strInput->hasAcceptableInput(), false);
2035     QCOMPARE(strInput->property("acceptable").toBool(), false);
2036     QTest::keyPress(&canvas, Qt::Key_1);
2037     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2038     QTest::qWait(50);
2039     QTRY_COMPARE(strInput->text(), QLatin1String(""));
2040     QCOMPARE(strInput->hasAcceptableInput(), false);
2041     QCOMPARE(strInput->property("acceptable").toBool(), false);
2042     QCOMPARE(strSpy.count(), 0);
2043     QTest::keyPress(&canvas, Qt::Key_A);
2044     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2045     QTest::qWait(50);
2046     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
2047     QCOMPARE(strInput->hasAcceptableInput(), false);
2048     QCOMPARE(strInput->property("acceptable").toBool(), false);
2049     QCOMPARE(strSpy.count(), 0);
2050     QTest::keyPress(&canvas, Qt::Key_A);
2051     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2052     QTest::qWait(50);
2053     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
2054     QCOMPARE(strInput->hasAcceptableInput(), true);
2055     QCOMPARE(strInput->property("acceptable").toBool(), true);
2056     QCOMPARE(strSpy.count(), 1);
2057     QTest::keyPress(&canvas, Qt::Key_A);
2058     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2059     QTest::qWait(50);
2060     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
2061     QCOMPARE(strInput->hasAcceptableInput(), true);
2062     QCOMPARE(strInput->property("acceptable").toBool(), true);
2063     QCOMPARE(strSpy.count(), 1);
2064     QTest::keyPress(&canvas, Qt::Key_A);
2065     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2066     QTest::qWait(50);
2067     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2068     QCOMPARE(strInput->hasAcceptableInput(), true);
2069     QCOMPARE(strInput->property("acceptable").toBool(), true);
2070     QCOMPARE(strSpy.count(), 1);
2071     QTest::keyPress(&canvas, Qt::Key_A);
2072     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2073     QTest::qWait(50);
2074     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
2075     QCOMPARE(strInput->hasAcceptableInput(), true);
2076     QCOMPARE(strInput->property("acceptable").toBool(), true);
2077     QCOMPARE(strSpy.count(), 1);
2078
2079     QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
2080     QVERIFY(unvalidatedInput);
2081     QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
2082     unvalidatedInput->setFocus(true);
2083     QVERIFY(unvalidatedInput->hasActiveFocus() == true);
2084     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2085     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2086     QTest::keyPress(&canvas, Qt::Key_1);
2087     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
2088     QTest::qWait(50);
2089     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
2090     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2091     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2092     QCOMPARE(unvalidatedSpy.count(), 0);
2093     QTest::keyPress(&canvas, Qt::Key_A);
2094     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2095     QTest::qWait(50);
2096     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
2097     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
2098     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
2099     QCOMPARE(unvalidatedSpy.count(), 0);
2100 }
2101
2102 void tst_qquicktextinput::inputMethods()
2103 {
2104     QQuickView canvas(testFileUrl("inputmethods.qml"));
2105     canvas.show();
2106     canvas.requestActivateWindow();
2107     QTest::qWaitForWindowShown(&canvas);
2108
2109     // test input method hints
2110     QVERIFY(canvas.rootObject() != 0);
2111     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
2112     QVERIFY(input != 0);
2113     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
2114     QSignalSpy inputMethodHintSpy(input, SIGNAL(inputMethodHintsChanged()));
2115     input->setInputMethodHints(Qt::ImhUppercaseOnly);
2116     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
2117     QCOMPARE(inputMethodHintSpy.count(), 1);
2118     input->setInputMethodHints(Qt::ImhUppercaseOnly);
2119     QCOMPARE(inputMethodHintSpy.count(), 1);
2120
2121     // default value
2122     QQuickTextInput plainInput;
2123     QCOMPARE(plainInput.inputMethodHints(), Qt::ImhNone);
2124
2125     input->setFocus(true);
2126     QVERIFY(input->hasActiveFocus() == true);
2127     // test that input method event is committed
2128     QInputMethodEvent event;
2129     event.setCommitString( "My ", -12, 0);
2130     QTRY_COMPARE(qGuiApp->focusObject(), input);
2131     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2132     QCOMPARE(input->text(), QString("My Hello world!"));
2133
2134     input->setCursorPosition(2);
2135     event.setCommitString("Your", -2, 2);
2136     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2137     QCOMPARE(input->text(), QString("Your Hello world!"));
2138     QCOMPARE(input->cursorPosition(), 4);
2139
2140     input->setCursorPosition(7);
2141     event.setCommitString("Goodbye", -2, 5);
2142     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2143     QCOMPARE(input->text(), QString("Your Goodbye world!"));
2144     QCOMPARE(input->cursorPosition(), 12);
2145
2146     input->setCursorPosition(8);
2147     event.setCommitString("Our", -8, 4);
2148     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2149     QCOMPARE(input->text(), QString("Our Goodbye world!"));
2150     QCOMPARE(input->cursorPosition(), 7);
2151
2152     // input should reset selection even if replacement parameters are out of bounds
2153     input->setText("text");
2154     input->setCursorPosition(0);
2155     input->moveCursorSelection(input->text().length());
2156     event.setCommitString("replacement", -input->text().length(), input->text().length());
2157     QGuiApplication::sendEvent(qGuiApp->focusObject(), &event);
2158     QCOMPARE(input->selectionStart(), input->selectionEnd());
2159
2160     QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
2161     QGuiApplication::sendEvent(input, &enabledQueryEvent);
2162     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
2163
2164     input->setReadOnly(true);
2165     QGuiApplication::sendEvent(input, &enabledQueryEvent);
2166     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
2167 }
2168
2169 /*
2170 TextInput element should only handle left/right keys until the cursor reaches
2171 the extent of the text, then they should ignore the keys.
2172
2173 */
2174 void tst_qquicktextinput::navigation()
2175 {
2176     QQuickView canvas(testFileUrl("navigation.qml"));
2177     canvas.show();
2178     canvas.requestActivateWindow();
2179
2180     QVERIFY(canvas.rootObject() != 0);
2181
2182     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2183
2184     QVERIFY(input != 0);
2185     input->setCursorPosition(0);
2186     QTRY_VERIFY(input->hasActiveFocus() == true);
2187     simulateKey(&canvas, Qt::Key_Left);
2188     QVERIFY(input->hasActiveFocus() == false);
2189     simulateKey(&canvas, Qt::Key_Right);
2190     QVERIFY(input->hasActiveFocus() == true);
2191     //QT-2944: If text is selected, ensure we deselect upon cursor motion
2192     input->setCursorPosition(input->text().length());
2193     input->select(0,input->text().length());
2194     QVERIFY(input->selectionStart() != input->selectionEnd());
2195     simulateKey(&canvas, Qt::Key_Right);
2196     QVERIFY(input->selectionStart() == input->selectionEnd());
2197     QVERIFY(input->selectionStart() == input->text().length());
2198     QVERIFY(input->hasActiveFocus() == true);
2199     simulateKey(&canvas, Qt::Key_Right);
2200     QVERIFY(input->hasActiveFocus() == false);
2201     simulateKey(&canvas, Qt::Key_Left);
2202     QVERIFY(input->hasActiveFocus() == true);
2203
2204     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2205     input->setCursorPosition(2);
2206     QCOMPARE(input->cursorPosition(),2);
2207     simulateKey(&canvas, Qt::Key_Up);
2208     QCOMPARE(input->cursorPosition(),2);
2209     simulateKey(&canvas, Qt::Key_Down);
2210     QCOMPARE(input->cursorPosition(),2);
2211
2212     // Test left and right navigation works if the TextInput is empty (QTBUG-25447).
2213     input->setText(QString());
2214     QCOMPARE(input->cursorPosition(), 0);
2215     simulateKey(&canvas, Qt::Key_Right);
2216     QCOMPARE(input->hasActiveFocus(), false);
2217     simulateKey(&canvas, Qt::Key_Left);
2218     QCOMPARE(input->hasActiveFocus(), true);
2219     simulateKey(&canvas, Qt::Key_Left);
2220     QCOMPARE(input->hasActiveFocus(), false);
2221 }
2222
2223 void tst_qquicktextinput::navigation_RTL()
2224 {
2225     QQuickView canvas(testFileUrl("navigation.qml"));
2226     canvas.show();
2227     canvas.requestActivateWindow();
2228
2229     QVERIFY(canvas.rootObject() != 0);
2230
2231     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2232
2233     QVERIFY(input != 0);
2234     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2235     input->setText(QString::fromUtf16(arabic_str, 11));
2236
2237     input->setCursorPosition(0);
2238     QTRY_VERIFY(input->hasActiveFocus() == true);
2239
2240     // move off
2241     simulateKey(&canvas, Qt::Key_Right);
2242     QVERIFY(input->hasActiveFocus() == false);
2243
2244     // move back
2245     simulateKey(&canvas, Qt::Key_Left);
2246     QVERIFY(input->hasActiveFocus() == true);
2247
2248     input->setCursorPosition(input->text().length());
2249     QVERIFY(input->hasActiveFocus() == true);
2250
2251     // move off
2252     simulateKey(&canvas, Qt::Key_Left);
2253     QVERIFY(input->hasActiveFocus() == false);
2254
2255     // move back
2256     simulateKey(&canvas, Qt::Key_Right);
2257     QVERIFY(input->hasActiveFocus() == true);
2258 }
2259
2260 void tst_qquicktextinput::copyAndPaste() {
2261 #ifndef QT_NO_CLIPBOARD
2262
2263 #ifdef Q_OS_MAC
2264     {
2265         PasteboardRef pasteboard;
2266         OSStatus status = PasteboardCreate(0, &pasteboard);
2267         if (status == noErr)
2268             CFRelease(pasteboard);
2269         else
2270             QSKIP("This machine doesn't support the clipboard");
2271     }
2272 #endif
2273
2274     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2275     QQmlComponent textInputComponent(&engine);
2276     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2277     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2278     QVERIFY(textInput != 0);
2279
2280     // copy and paste
2281     QCOMPARE(textInput->text().length(), 12);
2282     textInput->select(0, textInput->text().length());
2283     textInput->copy();
2284     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2285     QCOMPARE(textInput->selectedText().length(), 12);
2286     textInput->setCursorPosition(0);
2287     QVERIFY(textInput->canPaste());
2288     textInput->paste();
2289     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2290     QCOMPARE(textInput->text().length(), 24);
2291
2292     // can paste
2293     QVERIFY(textInput->canPaste());
2294     textInput->setReadOnly(true);
2295     QVERIFY(!textInput->canPaste());
2296     textInput->setReadOnly(false);
2297     QVERIFY(textInput->canPaste());
2298
2299     // select word
2300     textInput->setCursorPosition(0);
2301     textInput->selectWord();
2302     QCOMPARE(textInput->selectedText(), QString("Hello"));
2303
2304     // select all and cut
2305     textInput->selectAll();
2306     textInput->cut();
2307     QCOMPARE(textInput->text().length(), 0);
2308     textInput->paste();
2309     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2310     QCOMPARE(textInput->text().length(), 24);
2311
2312     // clear copy buffer
2313     QClipboard *clipboard = QGuiApplication::clipboard();
2314     QVERIFY(clipboard);
2315     clipboard->clear();
2316     QVERIFY(!textInput->canPaste());
2317
2318     // test that copy functionality is disabled
2319     // when echo mode is set to hide text/password mode
2320     int index = 0;
2321     while (index < 4) {
2322         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2323         textInput->setEchoMode(echoMode);
2324         textInput->setText("My password");
2325         textInput->select(0, textInput->text().length());
2326         textInput->copy();
2327         if (echoMode == QQuickTextInput::Normal) {
2328             QVERIFY(!clipboard->text().isEmpty());
2329             QCOMPARE(clipboard->text(), QString("My password"));
2330             clipboard->clear();
2331         } else {
2332             QVERIFY(clipboard->text().isEmpty());
2333         }
2334         index++;
2335     }
2336
2337     delete textInput;
2338 #endif
2339 }
2340
2341 void tst_qquicktextinput::copyAndPasteKeySequence() {
2342 #ifndef QT_NO_CLIPBOARD
2343
2344 #ifdef Q_OS_MAC
2345     {
2346         PasteboardRef pasteboard;
2347         OSStatus status = PasteboardCreate(0, &pasteboard);
2348         if (status == noErr)
2349             CFRelease(pasteboard);
2350         else
2351             QSKIP("This machine doesn't support the clipboard");
2352     }
2353 #endif
2354
2355     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2356     QQmlComponent textInputComponent(&engine);
2357     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2358     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2359     QVERIFY(textInput != 0);
2360
2361     QQuickCanvas canvas;
2362     textInput->setParentItem(canvas.rootItem());
2363     canvas.show();
2364     canvas.requestActivateWindow();
2365     QTest::qWaitForWindowShown(&canvas);
2366     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2367
2368     // copy and paste
2369     QVERIFY(textInput->hasActiveFocus());
2370     QCOMPARE(textInput->text().length(), 12);
2371     textInput->select(0, textInput->text().length());
2372     simulateKeys(&canvas, QKeySequence::Copy);
2373     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2374     QCOMPARE(textInput->selectedText().length(), 12);
2375     textInput->setCursorPosition(0);
2376     QVERIFY(textInput->canPaste());
2377     simulateKeys(&canvas, QKeySequence::Paste);
2378     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2379     QCOMPARE(textInput->text().length(), 24);
2380
2381     // select all and cut
2382     simulateKeys(&canvas, QKeySequence::SelectAll);
2383     simulateKeys(&canvas, QKeySequence::Cut);
2384     QCOMPARE(textInput->text().length(), 0);
2385     simulateKeys(&canvas, QKeySequence::Paste);
2386     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2387     QCOMPARE(textInput->text().length(), 24);
2388
2389     // clear copy buffer
2390     QClipboard *clipboard = QGuiApplication::clipboard();
2391     QVERIFY(clipboard);
2392     clipboard->clear();
2393     QVERIFY(!textInput->canPaste());
2394
2395     // test that copy functionality is disabled
2396     // when echo mode is set to hide text/password mode
2397     int index = 0;
2398     while (index < 4) {
2399         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2400         textInput->setEchoMode(echoMode);
2401         textInput->setText("My password");
2402         textInput->select(0, textInput->text().length());
2403         simulateKeys(&canvas, QKeySequence::Copy);
2404         if (echoMode == QQuickTextInput::Normal) {
2405             QVERIFY(!clipboard->text().isEmpty());
2406             QCOMPARE(clipboard->text(), QString("My password"));
2407             clipboard->clear();
2408         } else {
2409             QVERIFY(clipboard->text().isEmpty());
2410         }
2411         index++;
2412     }
2413
2414     delete textInput;
2415 #endif
2416 }
2417
2418 void tst_qquicktextinput::canPasteEmpty() {
2419 #ifndef QT_NO_CLIPBOARD
2420
2421     QGuiApplication::clipboard()->clear();
2422
2423     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2424     QQmlComponent textInputComponent(&engine);
2425     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2426     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2427     QVERIFY(textInput != 0);
2428
2429     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2430     QCOMPARE(textInput->canPaste(), cp);
2431
2432 #endif
2433 }
2434
2435 void tst_qquicktextinput::canPaste() {
2436 #ifndef QT_NO_CLIPBOARD
2437
2438     QGuiApplication::clipboard()->setText("Some text");
2439
2440     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2441     QQmlComponent textInputComponent(&engine);
2442     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2443     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2444     QVERIFY(textInput != 0);
2445
2446     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2447     QCOMPARE(textInput->canPaste(), cp);
2448
2449 #endif
2450 }
2451
2452 void tst_qquicktextinput::passwordCharacter()
2453 {
2454     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2455     QQmlComponent textInputComponent(&engine);
2456     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2457     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2458     QVERIFY(textInput != 0);
2459
2460     textInput->setPasswordCharacter("X");
2461     qreal implicitWidth = textInput->implicitWidth();
2462     textInput->setPasswordCharacter(".");
2463
2464     // QTBUG-12383 content is updated and redrawn
2465     QVERIFY(textInput->implicitWidth() < implicitWidth);
2466
2467     delete textInput;
2468 }
2469
2470 void tst_qquicktextinput::cursorDelegate_data()
2471 {
2472     QTest::addColumn<QUrl>("source");
2473     QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2474     QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2475     QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2476 }
2477
2478 void tst_qquicktextinput::cursorDelegate()
2479 {
2480     QFETCH(QUrl, source);
2481     QQuickView view(source);
2482     view.show();
2483     view.requestActivateWindow();
2484     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2485     QVERIFY(textInputObject != 0);
2486     // Delegate is created on demand, and so won't be available immediately.  Focus in or
2487     // setCursorVisible(true) will trigger creation.
2488     QTRY_VERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2489     QVERIFY(!textInputObject->isCursorVisible());
2490     //Test Delegate gets created
2491     textInputObject->setFocus(true);
2492     QVERIFY(textInputObject->isCursorVisible());
2493     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2494     QVERIFY(delegateObject);
2495     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2496     //Test Delegate gets moved
2497     for (int i=0; i<= textInputObject->text().length(); i++) {
2498         textInputObject->setCursorPosition(i);
2499         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2500         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2501     }
2502     textInputObject->setCursorPosition(0);
2503     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2504     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2505
2506     // Test delegate gets moved on mouse press.
2507     textInputObject->setSelectByMouse(true);
2508     textInputObject->setCursorPosition(0);
2509     const QPoint point1 = textInputObject->positionToRectangle(5).center().toPoint();
2510     QTest::qWait(400);  //ensure this isn't treated as a double-click
2511     QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2512     QTest::qWait(50);
2513     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2514     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2515     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2516
2517     // Test delegate gets moved on mouse drag
2518     textInputObject->setCursorPosition(0);
2519     const QPoint point2 = textInputObject->positionToRectangle(10).center().toPoint();
2520     QTest::qWait(400);  //ensure this isn't treated as a double-click
2521     QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2522     QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2523     QGuiApplication::sendEvent(&view, &mv);
2524     QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2525     QTest::qWait(50);
2526     QTRY_COMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2527     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2528
2529     textInputObject->setReadOnly(true);
2530     textInputObject->setCursorPosition(0);
2531     QTest::qWait(400);  //ensure this isn't treated as a double-click
2532     QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2533     QTest::qWait(50);
2534     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2535     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2536     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2537
2538     textInputObject->setCursorPosition(0);
2539     QTest::qWait(400);  //ensure this isn't treated as a double-click
2540     QTest::mouseClick(&view, Qt::LeftButton, 0, textInputObject->positionToRectangle(5).center().toPoint());
2541     QTest::qWait(50);
2542     QTRY_VERIFY(textInputObject->cursorPosition() != 0);
2543     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2544     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2545
2546     textInputObject->setCursorPosition(0);
2547     QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2548     QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2549
2550     // Delegate moved when text is entered
2551     textInputObject->setText(QString());
2552     for (int i = 0; i < 20; ++i) {
2553         QTest::keyClick(&view, Qt::Key_A);
2554         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2555         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2556     }
2557
2558     // Delegate moved when text is entered by im.
2559     textInputObject->setText(QString());
2560     for (int i = 0; i < 20; ++i) {
2561         QInputMethodEvent event;
2562         event.setCommitString("a");
2563         QGuiApplication::sendEvent(&view, &event);
2564         QCOMPARE(textInputObject->cursorRectangle().x(), delegateObject->x());
2565         QCOMPARE(textInputObject->cursorRectangle().y(), delegateObject->y());
2566     }
2567
2568     //Test Delegate gets deleted
2569     textInputObject->setCursorDelegate(0);
2570     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2571 }
2572
2573 void tst_qquicktextinput::cursorVisible()
2574 {
2575     QQuickTextInput input;
2576     input.componentComplete();
2577     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2578
2579     QQuickView view(testFileUrl("cursorVisible.qml"));
2580     view.show();
2581     view.requestActivateWindow();
2582     QTest::qWaitForWindowShown(&view);
2583     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2584
2585     QCOMPARE(input.isCursorVisible(), false);
2586
2587     input.setCursorVisible(true);
2588     QCOMPARE(input.isCursorVisible(), true);
2589     QCOMPARE(spy.count(), 1);
2590
2591     input.setCursorVisible(false);
2592     QCOMPARE(input.isCursorVisible(), false);
2593     QCOMPARE(spy.count(), 2);
2594
2595     input.setFocus(true);
2596     QCOMPARE(input.isCursorVisible(), false);
2597     QCOMPARE(spy.count(), 2);
2598
2599     input.setParentItem(view.rootObject());
2600     QCOMPARE(input.isCursorVisible(), true);
2601     QCOMPARE(spy.count(), 3);
2602
2603     input.setFocus(false);
2604     QCOMPARE(input.isCursorVisible(), false);
2605     QCOMPARE(spy.count(), 4);
2606
2607     input.setFocus(true);
2608     QCOMPARE(input.isCursorVisible(), true);
2609     QCOMPARE(spy.count(), 5);
2610
2611     QWindow alternateView;
2612     alternateView.show();
2613     alternateView.requestActivateWindow();
2614     QTest::qWaitForWindowShown(&alternateView);
2615
2616     QCOMPARE(input.isCursorVisible(), false);
2617     QCOMPARE(spy.count(), 6);
2618
2619     view.requestActivateWindow();
2620     QTest::qWaitForWindowShown(&view);
2621     QCOMPARE(input.isCursorVisible(), true);
2622     QCOMPARE(spy.count(), 7);
2623
2624     {   // Cursor attribute with 0 length hides cursor.
2625         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2626                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2627         QCoreApplication::sendEvent(&input, &ev);
2628     }
2629     QCOMPARE(input.isCursorVisible(), false);
2630     QCOMPARE(spy.count(), 8);
2631
2632     {   // Cursor attribute with non zero length shows cursor.
2633         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2634                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2635         QCoreApplication::sendEvent(&input, &ev);
2636     }
2637     QCOMPARE(input.isCursorVisible(), true);
2638     QCOMPARE(spy.count(), 9);
2639
2640     {   // If the cursor is hidden by the input method and the text is changed it should be visible again.
2641         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2642                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2643         QCoreApplication::sendEvent(&input, &ev);
2644     }
2645     QCOMPARE(input.isCursorVisible(), false);
2646     QCOMPARE(spy.count(), 10);
2647
2648     input.setText("something");
2649     QCOMPARE(input.isCursorVisible(), true);
2650     QCOMPARE(spy.count(), 11);
2651
2652     {   // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2653         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2654                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2655         QCoreApplication::sendEvent(&input, &ev);
2656     }
2657     QCOMPARE(input.isCursorVisible(), false);
2658     QCOMPARE(spy.count(), 12);
2659
2660     input.setCursorPosition(5);
2661     QCOMPARE(input.isCursorVisible(), true);
2662     QCOMPARE(spy.count(), 13);
2663 }
2664
2665 void tst_qquicktextinput::cursorRectangle_data()
2666 {
2667     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2668
2669     QTest::addColumn<QString>("text");
2670     QTest::addColumn<int>("positionAtWidth");
2671     QTest::addColumn<int>("wrapPosition");
2672     QTest::addColumn<QString>("shortText");
2673     QTest::addColumn<bool>("leftToRight");
2674
2675     QTest::newRow("left to right")
2676             << "Hello      World!" << 5 << 11
2677             << "Hi"
2678             << true;
2679     QTest::newRow("right to left")
2680             << QString::fromUtf16(arabic_str, lengthOf(arabic_str)) << 5 << 11
2681             << QString::fromUtf16(arabic_str, 3)
2682             << false;
2683 }
2684
2685 void tst_qquicktextinput::cursorRectangle()
2686 {
2687
2688     QFETCH(QString, text);
2689     QFETCH(int, positionAtWidth);
2690     QFETCH(int, wrapPosition);
2691     QFETCH(QString, shortText);
2692     QFETCH(bool, leftToRight);
2693
2694     QQuickTextInput input;
2695     input.setText(text);
2696     input.componentComplete();
2697
2698     QTextLayout layout(text);
2699     layout.setFont(input.font());
2700     if (!qmlDisableDistanceField()) {
2701         QTextOption option;
2702         option.setUseDesignMetrics(true);
2703         layout.setTextOption(option);
2704     }
2705     layout.beginLayout();
2706     QTextLine line = layout.createLine();
2707     layout.endLayout();
2708
2709     qreal offset = 0;
2710     if (leftToRight) {
2711         input.setWidth(line.cursorToX(positionAtWidth, QTextLine::Leading));
2712     } else {
2713         input.setWidth(line.horizontalAdvance() - line.cursorToX(positionAtWidth, QTextLine::Leading));
2714         offset = line.horizontalAdvance() - input.width();
2715     }
2716     input.setHeight(qCeil(line.height() * 3 / 2));
2717
2718     QRectF r;
2719
2720     for (int i = 0; i <= positionAtWidth; ++i) {
2721         input.setCursorPosition(i);
2722         r = input.cursorRectangle();
2723
2724         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2725         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2726         QCOMPARE(input.positionToRectangle(i), r);
2727     }
2728
2729     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2730     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2731
2732     for (int i = positionAtWidth + 1; i < text.length(); ++i) {
2733         input.setCursorPosition(i);
2734         QCOMPARE(r, input.cursorRectangle());
2735         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2736         QCOMPARE(input.positionToRectangle(i), r);
2737     }
2738
2739     for (int i = text.length() - 2; i >= 0; --i) {
2740         input.setCursorPosition(i);
2741         r = input.cursorRectangle();
2742         QCOMPARE(r.top(), 0.);
2743         if (leftToRight) {
2744             QVERIFY(r.right() >= 0);
2745         } else {
2746             QVERIFY(r.left() <= input.width());
2747         }
2748         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2749         QCOMPARE(input.positionToRectangle(i), r);
2750     }
2751
2752     // Check position with word wrap.
2753     input.setWrapMode(QQuickTextInput::WordWrap);
2754     input.setAutoScroll(false);
2755     for (int i = 0; i < wrapPosition; ++i) {
2756         input.setCursorPosition(i);
2757         r = input.cursorRectangle();
2758
2759         if (i > positionAtWidth)
2760             QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2761         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2762         QCOMPARE(r.top(), 0.);
2763         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2764         QCOMPARE(input.positionToRectangle(i), r);
2765     }
2766
2767     input.setCursorPosition(wrapPosition);
2768     r = input.cursorRectangle();
2769     if (leftToRight) {
2770         QCOMPARE(r.left(), 0.);
2771     } else {
2772         QCOMPARE(r.left(), input.width());
2773     }
2774     QVERIFY(r.top() >= line.height() - 1);
2775     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2776     QCOMPARE(input.positionToRectangle(11), r);
2777
2778     for (int i = wrapPosition + 1; i < text.length(); ++i) {
2779         input.setCursorPosition(i);
2780         r = input.cursorRectangle();
2781         QVERIFY(r.top() >= line.height() - 1);
2782         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2783         QCOMPARE(input.positionToRectangle(i), r);
2784     }
2785
2786     // Check vertical scrolling with word wrap.
2787     input.setAutoScroll(true);
2788     for (int i = 0; i <= positionAtWidth; ++i) {
2789         input.setCursorPosition(i);
2790         r = input.cursorRectangle();
2791
2792         QCOMPARE(r.left(), line.cursorToX(i, QTextLine::Leading) - offset);
2793         QCOMPARE(r.top(), 0.);
2794         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2795         QCOMPARE(input.positionToRectangle(i), r);
2796     }
2797
2798     // Whitespace doesn't wrap, so scroll horizontally until the until the cursor
2799     // reaches the next non-whitespace character.
2800     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2801     for (int i = positionAtWidth + 1; i < wrapPosition && leftToRight; ++i) {
2802         input.setCursorPosition(i);
2803         QCOMPARE(r, input.cursorRectangle());
2804         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2805         QCOMPARE(input.positionToRectangle(i), r);
2806     }
2807
2808     input.setCursorPosition(wrapPosition);
2809     r = input.cursorRectangle();
2810     if (leftToRight) {
2811         QCOMPARE(r.left(), 0.);
2812     } else {
2813         QCOMPARE(r.left(), input.width());
2814     }
2815     QVERIFY(r.bottom() >= input.height());
2816     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2817     QCOMPARE(input.positionToRectangle(11), r);
2818
2819     for (int i = wrapPosition + 1; i < text.length(); ++i) {
2820         input.setCursorPosition(i);
2821         r = input.cursorRectangle();
2822         QVERIFY(r.bottom() >= input.height());
2823         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2824         QCOMPARE(input.positionToRectangle(i), r);
2825     }
2826
2827     for (int i = text.length() - 2; i >= wrapPosition; --i) {
2828         input.setCursorPosition(i);
2829         r = input.cursorRectangle();
2830         QVERIFY(r.bottom() >= input.height());
2831         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2832         QCOMPARE(input.positionToRectangle(i), r);
2833     }
2834
2835     input.setCursorPosition(wrapPosition - 1);
2836     r = input.cursorRectangle();
2837     QCOMPARE(r.top(), 0.);
2838     QEXPECT_FAIL("right to left", "QTBUG-24801", Continue);
2839     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2840     QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2841     QCOMPARE(input.positionToRectangle(10), r);
2842
2843     for (int i = wrapPosition - 2; i >= positionAtWidth + 1; --i) {
2844         input.setCursorPosition(i);
2845         QCOMPARE(r, input.cursorRectangle());
2846         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2847         QCOMPARE(input.positionToRectangle(i), r);
2848     }
2849
2850     for (int i = positionAtWidth; i >= 0; --i) {
2851         input.setCursorPosition(i);
2852         r = input.cursorRectangle();
2853         QCOMPARE(r.top(), 0.);
2854         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRectF(), r);
2855         QCOMPARE(input.positionToRectangle(i), r);
2856     }
2857
2858     input.setText(shortText);
2859     input.setHAlign(leftToRight ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
2860     r = input.cursorRectangle();
2861     QCOMPARE(r.left(), leftToRight ? input.width() : 0);
2862 }
2863
2864 void tst_qquicktextinput::readOnly()
2865 {
2866     QQuickView canvas(testFileUrl("readOnly.qml"));
2867     canvas.show();
2868     canvas.requestActivateWindow();
2869
2870     QVERIFY(canvas.rootObject() != 0);
2871
2872     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2873
2874     QVERIFY(input != 0);
2875     QTRY_VERIFY(input->hasActiveFocus() == true);
2876     QVERIFY(input->isReadOnly() == true);
2877     QString initial = input->text();
2878     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2879         simulateKey(&canvas, k);
2880     simulateKey(&canvas, Qt::Key_Return);
2881     simulateKey(&canvas, Qt::Key_Space);
2882     simulateKey(&canvas, Qt::Key_Escape);
2883     QCOMPARE(input->text(), initial);
2884
2885     input->setCursorPosition(3);
2886     input->setReadOnly(false);
2887     QCOMPARE(input->isReadOnly(), false);
2888     QCOMPARE(input->cursorPosition(), input->text().length());
2889 }
2890
2891 void tst_qquicktextinput::echoMode()
2892 {
2893     QQuickView canvas(testFileUrl("echoMode.qml"));
2894     canvas.show();
2895     canvas.requestActivateWindow();
2896     QTest::qWaitForWindowShown(&canvas);
2897     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2898
2899     QVERIFY(canvas.rootObject() != 0);
2900
2901     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2902
2903     QVERIFY(input != 0);
2904     QTRY_VERIFY(input->hasActiveFocus() == true);
2905     QString initial = input->text();
2906     Qt::InputMethodHints ref;
2907     QCOMPARE(initial, QLatin1String("ABCDefgh"));
2908     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2909     QCOMPARE(input->displayText(), input->text());
2910     //Normal
2911     ref &= ~Qt::ImhHiddenText;
2912     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2913     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2914     input->setEchoMode(QQuickTextInput::NoEcho);
2915     QCOMPARE(input->text(), initial);
2916     QCOMPARE(input->displayText(), QLatin1String(""));
2917     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2918     //NoEcho
2919     ref |= Qt::ImhHiddenText;
2920     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2921     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2922     input->setEchoMode(QQuickTextInput::Password);
2923     //Password
2924     ref |= Qt::ImhHiddenText;
2925     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2926     QCOMPARE(input->text(), initial);
2927     QCOMPARE(input->displayText(), QLatin1String("********"));
2928     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2929     // clearing input hints do not clear bits set by echo mode
2930     input->setInputMethodHints(Qt::ImhNone);
2931     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2932     input->setPasswordCharacter(QChar('Q'));
2933     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2934     QCOMPARE(input->text(), initial);
2935     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2936     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2937     //PasswordEchoOnEdit
2938     ref &= ~Qt::ImhHiddenText;
2939     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2940     QCOMPARE((Qt::InputMethodHints) input->inputMethodQuery(Qt::ImHints).toInt(), ref);
2941     QCOMPARE(input->text(), initial);
2942     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2943     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2944     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2945     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2946     QCOMPARE(input->text(), QLatin1String("a"));
2947     QCOMPARE(input->displayText(), QLatin1String("a"));
2948     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2949     input->setFocus(false);
2950     QVERIFY(input->hasActiveFocus() == false);
2951     QCOMPARE(input->displayText(), QLatin1String("Q"));
2952     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2953     input->setFocus(true);
2954     QVERIFY(input->hasActiveFocus());
2955     QInputMethodEvent inputEvent;
2956     inputEvent.setCommitString(initial);
2957     QGuiApplication::sendEvent(input, &inputEvent);
2958     QCOMPARE(input->text(), initial);
2959     QCOMPARE(input->displayText(), initial);
2960     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2961 }
2962
2963 void tst_qquicktextinput::passwordEchoDelay()
2964 {
2965     int maskDelay = qGuiApp->styleHints()->passwordMaskDelay();
2966     if (maskDelay <= 0)
2967         QSKIP("No mask delay in use");
2968     QQuickView canvas(testFileUrl("echoMode.qml"));
2969     canvas.show();
2970     canvas.requestActivateWindow();
2971     QTest::qWaitForWindowShown(&canvas);
2972     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2973
2974     QVERIFY(canvas.rootObject() != 0);
2975
2976     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2977
2978     QChar fillChar = QLatin1Char('*');
2979
2980     input->setEchoMode(QQuickTextInput::Password);
2981     QCOMPARE(input->displayText(), QString(8, fillChar));
2982     input->setText(QString());
2983     QCOMPARE(input->displayText(), QString());
2984
2985     QTest::keyPress(&canvas, '0');
2986     QTest::keyPress(&canvas, '1');
2987     QTest::keyPress(&canvas, '2');
2988     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2989     QTest::keyPress(&canvas, '3');
2990     QTest::keyPress(&canvas, '4');
2991     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2992     QTest::keyPress(&canvas, Qt::Key_Backspace);
2993     QCOMPARE(input->displayText(), QString(4, fillChar));
2994     QTest::keyPress(&canvas, '4');
2995     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2996     QTest::qWait(maskDelay);
2997     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2998     QTest::keyPress(&canvas, '5');
2999     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
3000     input->setFocus(false);
3001     QVERIFY(!input->hasFocus());
3002     QCOMPARE(input->displayText(), QString(6, fillChar));
3003     input->setFocus(true);
3004     QTRY_VERIFY(input->hasFocus());
3005     QCOMPARE(input->displayText(), QString(6, fillChar));
3006     QTest::keyPress(&canvas, '6');
3007     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
3008
3009     QInputMethodEvent ev;
3010     ev.setCommitString(QLatin1String("7"));
3011     QGuiApplication::sendEvent(qGuiApp->focusObject(), &ev);
3012     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3013
3014     input->setCursorPosition(3);
3015     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
3016     QTest::keyPress(&canvas, 'a');
3017     QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
3018     QTest::keyPress(&canvas, Qt::Key_Backspace);
3019     QCOMPARE(input->displayText(), QString(8, fillChar));
3020 }
3021
3022
3023 void tst_qquicktextinput::simulateKey(QWindow *view, int key)
3024 {
3025     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
3026     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
3027
3028     QGuiApplication::sendEvent(view, &press);
3029     QGuiApplication::sendEvent(view, &release);
3030 }
3031
3032
3033 void tst_qquicktextinput::focusOnPress()
3034 {
3035     QString componentStr =
3036             "import QtQuick 2.0\n"
3037             "TextInput {\n"
3038                 "property bool selectOnFocus: false\n"
3039                 "width: 100; height: 50\n"
3040                 "activeFocusOnPress: true\n"
3041                 "text: \"Hello World\"\n"
3042                 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
3043             " }";
3044     QQmlComponent texteditComponent(&engine);
3045     texteditComponent.setData(componentStr.toLatin1(), QUrl());
3046     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput*>(texteditComponent.create());
3047     QVERIFY(textInputObject != 0);
3048     QCOMPARE(textInputObject->focusOnPress(), true);
3049     QCOMPARE(textInputObject->hasFocus(), false);
3050
3051     QSignalSpy focusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3052     QSignalSpy activeFocusSpy(textInputObject, SIGNAL(focusChanged(bool)));
3053     QSignalSpy activeFocusOnPressSpy(textInputObject, SIGNAL(activeFocusOnPressChanged(bool)));
3054
3055     textInputObject->setFocusOnPress(true);
3056     QCOMPARE(textInputObject->focusOnPress(), true);
3057     QCOMPARE(activeFocusOnPressSpy.count(), 0);
3058
3059     QQuickCanvas canvas;
3060     canvas.resize(100, 50);
3061     textInputObject->setParentItem(canvas.rootItem());
3062     canvas.show();
3063     canvas.requestActivateWindow();
3064     QTest::qWaitForWindowShown(&canvas);
3065     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3066
3067     QCOMPARE(textInputObject->hasFocus(), false);
3068     QCOMPARE(textInputObject->hasActiveFocus(), false);
3069
3070     QPoint centerPoint(canvas.width()/2, canvas.height()/2);
3071     Qt::KeyboardModifiers noModifiers = 0;
3072     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3073     QGuiApplication::processEvents();
3074     QCOMPARE(textInputObject->hasFocus(), true);
3075     QCOMPARE(textInputObject->hasActiveFocus(), true);
3076     QCOMPARE(focusSpy.count(), 1);
3077     QCOMPARE(activeFocusSpy.count(), 1);
3078     QCOMPARE(textInputObject->selectedText(), QString());
3079     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3080
3081     textInputObject->setFocusOnPress(false);
3082     QCOMPARE(textInputObject->focusOnPress(), false);
3083     QCOMPARE(activeFocusOnPressSpy.count(), 1);
3084
3085     textInputObject->setFocus(false);
3086     QCOMPARE(textInputObject->hasFocus(), false);
3087     QCOMPARE(textInputObject->hasActiveFocus(), false);
3088     QCOMPARE(focusSpy.count(), 2);
3089     QCOMPARE(activeFocusSpy.count(), 2);
3090
3091     // Wait for double click timeout to expire before clicking again.
3092     QTest::qWait(400);
3093     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3094     QGuiApplication::processEvents();
3095     QCOMPARE(textInputObject->hasFocus(), false);
3096     QCOMPARE(textInputObject->hasActiveFocus(), false);
3097     QCOMPARE(focusSpy.count(), 2);
3098     QCOMPARE(activeFocusSpy.count(), 2);
3099     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3100
3101     textInputObject->setFocusOnPress(true);
3102     QCOMPARE(textInputObject->focusOnPress(), true);
3103     QCOMPARE(activeFocusOnPressSpy.count(), 2);
3104
3105     // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
3106     textInputObject->setProperty("selectOnFocus", true);
3107
3108     QTest::qWait(400);
3109     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3110     QGuiApplication::processEvents();
3111     QCOMPARE(textInputObject->hasFocus(), true);
3112     QCOMPARE(textInputObject->hasActiveFocus(), true);
3113     QCOMPARE(focusSpy.count(), 3);
3114     QCOMPARE(activeFocusSpy.count(), 3);
3115     QCOMPARE(textInputObject->selectedText(), textInputObject->text());
3116     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
3117 }
3118
3119 void tst_qquicktextinput::openInputPanel()
3120 {
3121     PlatformInputContext platformInputContext;
3122     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3123     inputMethodPrivate->testContext = &platformInputContext;
3124
3125     QQuickView view(testFileUrl("openInputPanel.qml"));
3126     view.show();
3127     view.requestActivateWindow();
3128     QTest::qWaitForWindowShown(&view);
3129     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3130
3131     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3132     QVERIFY(input);
3133
3134     // check default values
3135     QVERIFY(input->focusOnPress());
3136     QVERIFY(!input->hasActiveFocus());
3137     QVERIFY(qApp->focusObject() != input);
3138     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3139
3140     // input panel should open on focus
3141     QPoint centerPoint(view.width()/2, view.height()/2);
3142     Qt::KeyboardModifiers noModifiers = 0;
3143     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3144     QGuiApplication::processEvents();
3145     QVERIFY(input->hasActiveFocus());
3146     QCOMPARE(qApp->focusObject(), input);
3147     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3148     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3149
3150     // input panel should be re-opened when pressing already focused TextInput
3151     qApp->inputMethod()->hide();
3152     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3153     QVERIFY(input->hasActiveFocus());
3154     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3155     QGuiApplication::processEvents();
3156     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3157     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3158
3159     // input panel should stay visible if focus is lost to another text inputor
3160     QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3161     QQuickTextInput anotherInput;
3162     anotherInput.componentComplete();
3163     anotherInput.setParentItem(view.rootObject());
3164     anotherInput.setFocus(true);
3165     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3166     QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherInput));
3167     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3168
3169     anotherInput.setFocus(false);
3170     QVERIFY(qApp->focusObject() != &anotherInput);
3171     QCOMPARE(view.activeFocusItem(), view.rootItem());
3172     anotherInput.setFocus(true);
3173
3174     qApp->inputMethod()->hide();
3175
3176     // input panel should not be opened if TextInput is read only
3177     input->setReadOnly(true);
3178     input->setFocus(true);
3179     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3180     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3181     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3182     QGuiApplication::processEvents();
3183     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3184
3185     // input panel should not be opened if focusOnPress is set to false
3186     input->setFocusOnPress(false);
3187     input->setFocus(false);
3188     input->setFocus(true);
3189     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3190     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
3191     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
3192     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3193
3194     // input panel should open when openSoftwareInputPanel is called
3195     input->openSoftwareInputPanel();
3196     QCOMPARE(qApp->inputMethod()->isVisible(), true);
3197
3198     // input panel should close when closeSoftwareInputPanel is called
3199     input->closeSoftwareInputPanel();
3200     QCOMPARE(qApp->inputMethod()->isVisible(), false);
3201 }
3202
3203 class MyTextInput : public QQuickTextInput
3204 {
3205 public:
3206     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
3207     {
3208         nbPaint = 0;
3209     }
3210     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
3211     {
3212        nbPaint++;
3213        return QQuickTextInput::updatePaintNode(node, data);
3214     }
3215     int nbPaint;
3216 };
3217
3218 void tst_qquicktextinput::setHAlignClearCache()
3219 {
3220     QQuickView view;
3221     MyTextInput input;
3222     input.setText("Hello world");
3223     input.setParentItem(view.rootItem());
3224     view.show();
3225     view.requestActivateWindow();
3226     QTest::qWaitForWindowShown(&view);
3227 #ifdef Q_OS_MAC
3228     QEXPECT_FAIL("", "QTBUG-23485", Abort);
3229 #endif
3230     QTRY_COMPARE(input.nbPaint, 1);
3231     input.setHAlign(QQuickTextInput::AlignRight);
3232     //Changing the alignment should trigger a repaint
3233     QTRY_COMPARE(input.nbPaint, 2);
3234 }
3235
3236 void tst_qquicktextinput::focusOutClearSelection()
3237 {
3238     QQuickView view;
3239     QQuickTextInput input;
3240     QQuickTextInput input2;
3241     input.setText(QLatin1String("Hello world"));
3242     input.setFocus(true);
3243     input2.setParentItem(view.rootItem());
3244     input.setParentItem(view.rootItem());
3245     input.componentComplete();
3246     input2.componentComplete();
3247     view.show();
3248     view.requestActivateWindow();
3249     QTest::qWaitForWindowShown(&view);
3250     input.select(2,5);
3251     //The selection should work
3252     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
3253     input2.setFocus(true);
3254     QGuiApplication::processEvents();
3255     //The input lost the focus selection should be cleared
3256     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
3257 }
3258
3259 void tst_qquicktextinput::geometrySignals()
3260 {
3261     QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
3262     QObject *o = component.create();
3263     QVERIFY(o);
3264     QCOMPARE(o->property("bindingWidth").toInt(), 400);
3265     QCOMPARE(o->property("bindingHeight").toInt(), 500);
3266     delete o;
3267 }
3268
3269 void tst_qquicktextinput::contentSize()
3270 {
3271     QString componentStr = "import QtQuick 2.0\nTextInput { width: 75; height: 16; font.pixelSize: 10 }";
3272     QQmlComponent textComponent(&engine);
3273     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
3274     QScopedPointer<QObject> object(textComponent.create());
3275     QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
3276
3277     QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3278
3279     textObject->setText("The quick red fox jumped over the lazy brown dog");
3280
3281     QVERIFY(textObject->contentWidth() > textObject->width());
3282     QVERIFY(textObject->contentHeight() < textObject->height());
3283     QCOMPARE(spy.count(), 1);
3284
3285     textObject->setWrapMode(QQuickTextInput::WordWrap);
3286     QVERIFY(textObject->contentWidth() <= textObject->width());
3287     QVERIFY(textObject->contentHeight() > textObject->height());
3288     QCOMPARE(spy.count(), 2);
3289
3290     textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3291
3292     QVERIFY(textObject->contentWidth() > textObject->width());
3293     QVERIFY(textObject->contentHeight() > textObject->height());
3294     QCOMPARE(spy.count(), 3);
3295
3296     textObject->setText("The quick red fox jumped over the lazy brown dog");
3297     for (int w = 60; w < 120; ++w) {
3298         textObject->setWidth(w);
3299         QVERIFY(textObject->contentWidth() <= textObject->width());
3300         QVERIFY(textObject->contentHeight() > textObject->height());
3301     }
3302 }
3303
3304 static void sendPreeditText(const QString &text, int cursor)
3305 {
3306     QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
3307             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
3308     QCoreApplication::sendEvent(qGuiApp->focusObject(), &event);
3309 }
3310
3311 void tst_qquicktextinput::preeditAutoScroll()
3312 {
3313     QString preeditText = "califragisiticexpialidocious!";
3314
3315     QQuickView view(testFileUrl("preeditAutoScroll.qml"));
3316     view.show();
3317     view.requestActivateWindow();
3318     QTest::qWaitForWindowShown(&view);
3319     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3320     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3321     QVERIFY(input);
3322     QVERIFY(input->hasActiveFocus());
3323
3324     input->setWidth(input->implicitWidth());
3325
3326     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
3327     int cursorRectangleChanges = 0;
3328
3329     // test the text is scrolled so the preedit is visible.
3330     sendPreeditText(preeditText.mid(0, 3), 1);
3331     QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
3332     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
3333     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3334
3335     // test the text is scrolled back when the preedit is removed.
3336     QInputMethodEvent imEvent;
3337     QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3338     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3339     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3340     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3341
3342     QTextLayout layout(preeditText);
3343     layout.setFont(input->font());
3344     if (!qmlDisableDistanceField()) {
3345         QTextOption option;
3346         option.setUseDesignMetrics(true);
3347         layout.setTextOption(option);
3348     }
3349     layout.beginLayout();
3350     QTextLine line = layout.createLine();
3351     layout.endLayout();
3352
3353     // test if the preedit is larger than the text input that the
3354     // character preceding the cursor is still visible.
3355     qreal x = input->positionToRectangle(0).x();
3356     for (int i = 0; i < 3; ++i) {
3357         sendPreeditText(preeditText, i + 1);
3358         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3359         QVERIFY(input->cursorRectangle().right() >= width - 3);
3360         QVERIFY(input->positionToRectangle(0).x() < x);
3361         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3362         x = input->positionToRectangle(0).x();
3363     }
3364     for (int i = 1; i >= 0; --i) {
3365         sendPreeditText(preeditText, i + 1);
3366         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
3367         QVERIFY(input->cursorRectangle().right() >= width - 3);
3368         QVERIFY(input->positionToRectangle(0).x() > x);
3369         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3370         x = input->positionToRectangle(0).x();
3371     }
3372
3373     // Test incrementing the preedit cursor doesn't cause further
3374     // scrolling when right most text is visible.
3375     sendPreeditText(preeditText, preeditText.length() - 3);
3376     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3377     x = input->positionToRectangle(0).x();
3378     for (int i = 2; i >= 0; --i) {
3379         sendPreeditText(preeditText, preeditText.length() - i);
3380         QCOMPARE(input->positionToRectangle(0).x(), x);
3381         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3382     }
3383     for (int i = 1; i <  3; ++i) {
3384         sendPreeditText(preeditText, preeditText.length() - i);
3385         QCOMPARE(input->positionToRectangle(0).x(), x);
3386         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
3387     }
3388
3389     // Test disabling auto scroll.
3390     QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent);
3391
3392     input->setAutoScroll(false);
3393     sendPreeditText(preeditText.mid(0, 3), 1);
3394     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
3395     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
3396 }
3397
3398 void tst_qquicktextinput::preeditCursorRectangle()
3399 {
3400     QString preeditText = "super";
3401
3402     QQuickView view(testFileUrl("inputMethodEvent.qml"));
3403     view.show();
3404     view.requestActivateWindow();
3405     QTest::qWaitForWindowShown(&view);
3406     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3407     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3408     QVERIFY(input);
3409
3410     QQuickItem *cursor = input->findChild<QQuickItem *>("cursor");
3411     QVERIFY(cursor);
3412
3413     QRectF currentRect;
3414
3415     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3416     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3417     QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3418
3419     // Verify that the micro focus rect is positioned the same for position 0 as
3420     // it would be if there was no preedit text.
3421     sendPreeditText(preeditText, 0);
3422     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3423     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3424     QCOMPARE(currentRect, previousRect);
3425     QCOMPARE(input->cursorRectangle(), currentRect);
3426     QCOMPARE(cursor->pos(), currentRect.topLeft());
3427
3428     QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
3429     QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3430
3431     // Verify that the micro focus rect moves to the left as the cursor position
3432     // is incremented.
3433     for (int i = 1; i <= 5; ++i) {
3434         sendPreeditText(preeditText, i);
3435         QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3436         currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3437         QVERIFY(previousRect.left() < currentRect.left());
3438         QCOMPARE(input->cursorRectangle(), currentRect);
3439         QCOMPARE(cursor->pos(), currentRect.topLeft());
3440         QVERIFY(inputSpy.count() > 0); inputSpy.clear();
3441         QVERIFY(panelSpy.count() > 0); panelSpy.clear();
3442         previousRect = currentRect;
3443     }
3444
3445     // Verify that if the cursor rectangle is updated if the pre-edit text changes
3446     // but the (non-zero) cursor position is the same.
3447     inputSpy.clear();
3448     panelSpy.clear();
3449     sendPreeditText("wwwww", 5);
3450     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3451     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3452     QCOMPARE(input->cursorRectangle(), currentRect);
3453     QCOMPARE(cursor->pos(), currentRect.topLeft());
3454     QCOMPARE(inputSpy.count(), 1);
3455     QCOMPARE(panelSpy.count(), 1);
3456
3457     // Verify that if there is no preedit cursor then the micro focus rect is the
3458     // same as it would be if it were positioned at the end of the preedit text.
3459     inputSpy.clear();
3460     panelSpy.clear();
3461     {   QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3462         QCoreApplication::sendEvent(qGuiApp->focusObject(), &imEvent); }
3463     QCoreApplication::sendEvent(qGuiApp->focusObject(), &query);
3464     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3465     QCOMPARE(currentRect, previousRect);
3466     QCOMPARE(input->cursorRectangle(), currentRect);
3467     QCOMPARE(cursor->pos(), currentRect.topLeft());
3468     QCOMPARE(inputSpy.count(), 1);
3469     QCOMPARE(panelSpy.count(), 1);
3470 }
3471
3472 void tst_qquicktextinput::inputContextMouseHandler()
3473 {
3474     PlatformInputContext platformInputContext;
3475     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3476     inputMethodPrivate->testContext = &platformInputContext;
3477
3478     QString text = "supercalifragisiticexpialidocious!";
3479     QQuickView view(testFileUrl("inputContext.qml"));
3480     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3481     QVERIFY(input);
3482
3483     input->setFocus(true);
3484     input->setText("");
3485
3486     view.show();
3487     view.requestActivateWindow();
3488     QTest::qWaitForWindowShown(&view);
3489     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3490
3491     QTextLayout layout(text);
3492     layout.setFont(input->font());
3493     if (!qmlDisableDistanceField()) {
3494         QTextOption option;
3495         option.setUseDesignMetrics(true);
3496         layout.setTextOption(option);
3497     }
3498     layout.beginLayout();
3499     QTextLine line = layout.createLine();
3500     layout.endLayout();
3501
3502     const qreal x = line.cursorToX(2, QTextLine::Leading);
3503     const qreal y = line.height() / 2;
3504     QPoint position = QPointF(x, y).toPoint();
3505
3506     QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
3507     QGuiApplication::sendEvent(input, &inputEvent);
3508
3509     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
3510     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
3511     QGuiApplication::processEvents();
3512
3513     QCOMPARE(platformInputContext.m_action, QInputMethod::Click);
3514     QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
3515     QCOMPARE(platformInputContext.m_cursorPosition, 2);
3516 }
3517
3518 void tst_qquicktextinput::inputMethodComposing()
3519 {
3520     QString text = "supercalifragisiticexpialidocious!";
3521
3522     QQuickView view(testFileUrl("inputContext.qml"));
3523     view.show();
3524     view.requestActivateWindow();
3525     QTest::qWaitForWindowShown(&view);
3526     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3527     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3528     QVERIFY(input);
3529     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3530
3531     QCOMPARE(input->isInputMethodComposing(), false);
3532     {
3533         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3534         QGuiApplication::sendEvent(input, &event);
3535     }
3536     QCOMPARE(input->isInputMethodComposing(), true);
3537     QCOMPARE(spy.count(), 1);
3538
3539     {
3540         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3541         QGuiApplication::sendEvent(input, &event);
3542     }
3543     QCOMPARE(spy.count(), 1);
3544
3545     {
3546         QInputMethodEvent event;
3547         QGuiApplication::sendEvent(input, &event);
3548     }
3549     QCOMPARE(input->isInputMethodComposing(), false);
3550     QCOMPARE(spy.count(), 2);
3551
3552     // Changing the text while not composing doesn't alter the composing state.
3553     input->setText(text.mid(0, 16));
3554     QCOMPARE(input->isInputMethodComposing(), false);
3555     QCOMPARE(spy.count(), 2);
3556
3557     {
3558         QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3559         QGuiApplication::sendEvent(input, &event);
3560     }
3561     QCOMPARE(input->isInputMethodComposing(), true);
3562     QCOMPARE(spy.count(), 3);
3563
3564     // Changing the text while composing cancels composition.
3565     input->setText(text.mid(0, 12));
3566     QCOMPARE(input->isInputMethodComposing(), false);
3567     QCOMPARE(spy.count(), 4);
3568
3569     {   // Preedit cursor positioned outside (empty) preedit; composing.
3570         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3571                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3572         QGuiApplication::sendEvent(input, &event);
3573     }
3574     QCOMPARE(input->isInputMethodComposing(), true);
3575     QCOMPARE(spy.count(), 5);
3576
3577
3578     {   // Cursor hidden; composing
3579         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3580                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3581         QGuiApplication::sendEvent(input, &event);
3582     }
3583     QCOMPARE(input->isInputMethodComposing(), true);
3584     QCOMPARE(spy.count(), 5);
3585
3586     {   // Default cursor attributes; composing.
3587         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3588                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3589         QGuiApplication::sendEvent(input, &event);
3590     }
3591     QCOMPARE(input->isInputMethodComposing(), true);
3592     QCOMPARE(spy.count(), 5);
3593
3594     {   // Selections are persisted: not composing
3595         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3596                 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant()));
3597         QGuiApplication::sendEvent(input, &event);
3598     }
3599     QCOMPARE(input->isInputMethodComposing(), false);
3600     QCOMPARE(spy.count(), 6);
3601
3602     input->setCursorPosition(12);
3603
3604     {   // Formatting applied; composing.
3605         QTextCharFormat format;
3606         format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3607         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3608                 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format));
3609         QGuiApplication::sendEvent(input, &event);
3610     }
3611     QCOMPARE(input->isInputMethodComposing(), true);
3612     QCOMPARE(spy.count(), 7);
3613
3614     {
3615         QInputMethodEvent event;
3616         QGuiApplication::sendEvent(input, &event);
3617     }
3618     QCOMPARE(input->isInputMethodComposing(), false);
3619     QCOMPARE(spy.count(), 8);
3620 }
3621
3622 void tst_qquicktextinput::inputMethodUpdate()
3623 {
3624     PlatformInputContext platformInputContext;
3625     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3626     inputMethodPrivate->testContext = &platformInputContext;
3627
3628     QQuickView view(testFileUrl("inputContext.qml"));
3629     view.show();
3630     view.requestActivateWindow();
3631     QTest::qWaitForWindowShown(&view);
3632     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3633     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3634     QVERIFY(input);
3635
3636     // text change even without cursor position change needs to trigger update
3637     input->setText("test");
3638     platformInputContext.clear();
3639     input->setText("xxxx");
3640     QVERIFY(platformInputContext.m_updateCallCount > 0);
3641
3642     // input method event replacing text
3643     platformInputContext.clear();
3644     {
3645         QInputMethodEvent inputMethodEvent;
3646         inputMethodEvent.setCommitString("y", -1, 1);
3647         QGuiApplication::sendEvent(input, &inputMethodEvent);
3648     }
3649     QVERIFY(platformInputContext.m_updateCallCount > 0);
3650
3651     // input method changing selection
3652     platformInputContext.clear();
3653     {
3654         QList<QInputMethodEvent::Attribute> attributes;
3655         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3656         QInputMethodEvent inputMethodEvent("", attributes);
3657         QGuiApplication::sendEvent(input, &inputMethodEvent);
3658     }
3659     QVERIFY(input->selectionStart() != input->selectionEnd());
3660     QVERIFY(platformInputContext.m_updateCallCount > 0);
3661
3662     // programmatical selections trigger update
3663     platformInputContext.clear();
3664     input->selectAll();
3665     QVERIFY(platformInputContext.m_updateCallCount > 0);
3666
3667     // font changes
3668     platformInputContext.clear();
3669     QFont font = input->font();
3670     font.setBold(!font.bold());
3671     input->setFont(font);
3672     QVERIFY(platformInputContext.m_updateCallCount > 0);
3673
3674     // normal input
3675     platformInputContext.clear();
3676     {
3677         QInputMethodEvent inputMethodEvent;
3678         inputMethodEvent.setCommitString("y");
3679         QGuiApplication::sendEvent(input, &inputMethodEvent);
3680     }
3681     QVERIFY(platformInputContext.m_updateCallCount > 0);
3682
3683     // changing cursor position
3684     platformInputContext.clear();
3685     input->setCursorPosition(0);
3686     QVERIFY(platformInputContext.m_updateCallCount > 0);
3687
3688     // read only disabled input method
3689     platformInputContext.clear();
3690     input->setReadOnly(true);
3691     QVERIFY(platformInputContext.m_updateCallCount > 0);
3692     input->setReadOnly(false);
3693
3694     // no updates while no focus
3695     input->setFocus(false);
3696     platformInputContext.clear();
3697     input->setText("Foo");
3698     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3699     input->setCursorPosition(1);
3700     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3701     input->selectAll();
3702     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3703     input->setReadOnly(true);
3704     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3705 }
3706
3707 void tst_qquicktextinput::cursorRectangleSize()
3708 {
3709     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3710     QVERIFY(canvas->rootObject() != 0);
3711     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3712
3713     // make sure cursor rectangle is not at (0,0)
3714     textInput->setX(10);
3715     textInput->setY(10);
3716     textInput->setCursorPosition(3);
3717     QVERIFY(textInput != 0);
3718     textInput->setFocus(true);
3719     canvas->show();
3720     canvas->requestActivateWindow();
3721     QTest::qWaitForWindowShown(canvas);
3722     QTRY_VERIFY(qApp->focusObject());
3723
3724     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3725     qApp->sendEvent(qApp->focusObject(), &event);
3726     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3727
3728     QRectF cursorRectFromItem = textInput->cursorRectangle();
3729     QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3730
3731     // item and input query cursor rectangles match
3732     QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3733
3734     // item cursor rectangle and positionToRectangle calculations match
3735     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3736
3737     // item-canvas transform and input item transform match
3738     QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3739
3740     // input panel cursorRectangle property and tranformed item cursor rectangle match
3741     QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3742     QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3743
3744     delete canvas;
3745 }
3746
3747 void tst_qquicktextinput::tripleClickSelectsAll()
3748 {
3749     QString qmlfile = testFile("positionAt.qml");
3750     QQuickView view(QUrl::fromLocalFile(qmlfile));
3751     view.show();
3752     view.requestActivateWindow();
3753     QTest::qWaitForWindowShown(&view);
3754
3755     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3756
3757     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3758     QVERIFY(input);
3759
3760     QLatin1String hello("Hello world!");
3761     input->setSelectByMouse(true);
3762     input->setText(hello);
3763
3764     // Clicking on the same point inside TextInput three times in a row
3765     // should trigger a triple click, thus selecting all the text.
3766     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3767     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3768     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3769     QGuiApplication::processEvents();
3770     QCOMPARE(input->selectedText(), hello);
3771
3772     // Now it simulates user moving the mouse between the second and the third click.
3773     // In this situation, we don't expect a triple click.
3774     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3775     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3776     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3777     QGuiApplication::processEvents();
3778     QVERIFY(input->selectedText().isEmpty());
3779
3780     // And now we press the third click too late, so no triple click event is triggered.
3781     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3782     QGuiApplication::processEvents();
3783     QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3784     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3785     QGuiApplication::processEvents();
3786     QVERIFY(input->selectedText().isEmpty());
3787 }
3788
3789 void tst_qquicktextinput::QTBUG_19956_data()
3790 {
3791     QTest::addColumn<QString>("url");
3792     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3793     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3794 }
3795
3796
3797 void tst_qquicktextinput::getText_data()
3798 {
3799     QTest::addColumn<QString>("text");
3800     QTest::addColumn<QString>("inputMask");
3801     QTest::addColumn<int>("start");
3802     QTest::addColumn<int>("end");
3803     QTest::addColumn<QString>("expectedText");
3804
3805     QTest::newRow("all plain text")
3806             << standard.at(0)
3807             << QString()
3808             << 0 << standard.at(0).length()
3809             << standard.at(0);
3810
3811     QTest::newRow("plain text sub string")
3812             << standard.at(0)
3813             << QString()
3814             << 0 << 12
3815             << standard.at(0).mid(0, 12);
3816
3817     QTest::newRow("plain text sub string reversed")
3818             << standard.at(0)
3819             << QString()
3820             << 12 << 0
3821             << standard.at(0).mid(0, 12);
3822
3823     QTest::newRow("plain text cropped beginning")
3824             << standard.at(0)
3825             << QString()
3826             << -3 << 4
3827             << standard.at(0).mid(0, 4);
3828
3829     QTest::newRow("plain text cropped end")
3830             << standard.at(0)
3831             << QString()
3832             << 23 << standard.at(0).length() + 8
3833             << standard.at(0).mid(23);
3834
3835     QTest::newRow("plain text cropped beginning and end")
3836             << standard.at(0)
3837             << QString()
3838             << -9 << standard.at(0).length() + 4
3839             << standard.at(0);
3840 }
3841
3842 void tst_qquicktextinput::getText()
3843 {
3844     QFETCH(QString, text);
3845     QFETCH(QString, inputMask);
3846     QFETCH(int, start);
3847     QFETCH(int, end);
3848     QFETCH(QString, expectedText);
3849
3850     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3851     QQmlComponent textInputComponent(&engine);
3852     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3853     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3854     QVERIFY(textInput != 0);
3855
3856     QCOMPARE(textInput->getText(start, end), expectedText);
3857 }
3858
3859 void tst_qquicktextinput::insert_data()
3860 {
3861     QTest::addColumn<QString>("text");
3862     QTest::addColumn<QString>("inputMask");
3863     QTest::addColumn<int>("selectionStart");
3864     QTest::addColumn<int>("selectionEnd");
3865     QTest::addColumn<int>("insertPosition");
3866     QTest::addColumn<QString>("insertText");
3867     QTest::addColumn<QString>("expectedText");
3868     QTest::addColumn<int>("expectedSelectionStart");
3869     QTest::addColumn<int>("expectedSelectionEnd");
3870     QTest::addColumn<int>("expectedCursorPosition");
3871     QTest::addColumn<bool>("selectionChanged");
3872     QTest::addColumn<bool>("cursorPositionChanged");
3873
3874     QTest::newRow("at cursor position (beginning)")
3875             << standard.at(0)
3876             << QString()
3877             << 0 << 0 << 0
3878             << QString("Hello")
3879             << QString("Hello") + standard.at(0)
3880             << 5 << 5 << 5
3881             << false << true;
3882
3883     QTest::newRow("at cursor position (end)")
3884             << standard.at(0)
3885             << QString()
3886             << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3887             << QString("Hello")
3888             << standard.at(0) + QString("Hello")
3889             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3890             << false << true;
3891
3892     QTest::newRow("at cursor position (middle)")
3893             << standard.at(0)
3894             << QString()
3895             << 18 << 18 << 18
3896             << QString("Hello")
3897             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3898             << 23 << 23 << 23
3899             << false << true;
3900
3901     QTest::newRow("after cursor position (beginning)")
3902             << standard.at(0)
3903             << QString()
3904             << 0 << 0 << 18
3905             << QString("Hello")
3906             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3907             << 0 << 0 << 0
3908             << false << false;
3909
3910     QTest::newRow("before cursor position (end)")
3911             << standard.at(0)
3912             << QString()
3913             << standard.at(0).length() << standard.at(0).length() << 18
3914             << QString("Hello")
3915             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3916             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3917             << false << true;
3918
3919     QTest::newRow("before cursor position (middle)")
3920             << standard.at(0)
3921             << QString()
3922             << 18 << 18 << 0
3923             << QString("Hello")
3924             << QString("Hello") + standard.at(0)
3925             << 23 << 23 << 23
3926             << false << true;
3927
3928     QTest::newRow("after cursor position (middle)")
3929             << standard.at(0)
3930             << QString()
3931             << 18 << 18 << standard.at(0).length()
3932             << QString("Hello")
3933             << standard.at(0) + QString("Hello")
3934             << 18 << 18 << 18
3935             << false << false;
3936
3937     QTest::newRow("before selection")
3938             << standard.at(0)
3939             << QString()
3940             << 14 << 19 << 0
3941             << QString("Hello")
3942             << QString("Hello") + standard.at(0)
3943             << 19 << 24 << 24
3944             << false << true;
3945
3946     QTest::newRow("before reversed selection")
3947             << standard.at(0)
3948             << QString()
3949             << 19 << 14 << 0
3950             << QString("Hello")
3951             << QString("Hello") + standard.at(0)
3952             << 19 << 24 << 19
3953             << false << true;
3954
3955     QTest::newRow("after selection")
3956             << standard.at(0)
3957             << QString()
3958             << 14 << 19 << standard.at(0).length()
3959             << QString("Hello")
3960             << standard.at(0) + QString("Hello")
3961             << 14 << 19 << 19
3962             << false << false;
3963
3964     QTest::newRow("after reversed selection")
3965             << standard.at(0)
3966             << QString()
3967             << 19 << 14 << standard.at(0).length()
3968             << QString("Hello")
3969             << standard.at(0) + QString("Hello")
3970             << 14 << 19 << 14
3971             << false << false;
3972
3973     QTest::newRow("into selection")
3974             << standard.at(0)
3975             << QString()
3976             << 14 << 19 << 18
3977             << QString("Hello")
3978             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3979             << 14 << 24 << 24
3980             << true << true;
3981
3982     QTest::newRow("into reversed selection")
3983             << standard.at(0)
3984             << QString()
3985             << 19 << 14 << 18
3986             << QString("Hello")
3987             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3988             << 14 << 24 << 14
3989             << true << false;
3990
3991     QTest::newRow("rich text into plain text")
3992             << standard.at(0)
3993             << QString()
3994             << 0 << 0 << 0
3995             << QString("<b>Hello</b>")
3996             << QString("<b>Hello</b>") + standard.at(0)
3997             << 12 << 12 << 12
3998             << false << true;
3999
4000     QTest::newRow("before start")
4001             << standard.at(0)
4002             << QString()
4003             << 0 << 0 << -3
4004             << QString("Hello")
4005             << standard.at(0)
4006             << 0 << 0 << 0
4007             << false << false;
4008
4009     QTest::newRow("past end")
4010             << standard.at(0)
4011             << QString()
4012             << 0 << 0 << standard.at(0).length() + 3
4013             << QString("Hello")
4014             << standard.at(0)
4015             << 0 << 0 << 0
4016             << false << false;
4017
4018     const QString inputMask = "009.009.009.009";
4019     const QString ip = "192.168.2.14";
4020
4021     QTest::newRow("mask: at cursor position (beginning)")
4022             << ip
4023             << inputMask
4024             << 0 << 0 << 0
4025             << QString("125")
4026             << QString("125.168.2.14")
4027             << 0 << 0 << 0
4028             << false << false;
4029
4030     QTest::newRow("mask: at cursor position (end)")
4031             << ip
4032             << inputMask
4033             << inputMask.length() << inputMask.length() << inputMask.length()
4034             << QString("8")
4035             << ip
4036             << inputMask.length() << inputMask.length() << inputMask.length()
4037             << false << false;
4038
4039     QTest::newRow("mask: at cursor position (middle)")
4040             << ip
4041             << inputMask
4042             << 6 << 6 << 6
4043             << QString("75.2")
4044             << QString("192.167.5.24")
4045             << 6 << 6 << 6
4046             << false << false;
4047
4048     QTest::newRow("mask: after cursor position (beginning)")
4049             << ip
4050             << inputMask
4051             << 0 << 0 << 6
4052             << QString("75.2")
4053             << QString("192.167.5.24")
4054             << 0 << 0 << 0
4055             << false << false;
4056
4057     QTest::newRow("mask: before cursor position (end)")
4058             << ip
4059             << inputMask
4060             << inputMask.length() << inputMask.length() << 6
4061             << QString("75.2")
4062             << QString("192.167.5.24")
4063             << inputMask.length() << inputMask.length() << inputMask.length()
4064             << false << false;
4065
4066     QTest::newRow("mask: before cursor position (middle)")
4067             << ip
4068             << inputMask
4069             << 6 << 6 << 0
4070             << QString("125")
4071             << QString("125.168.2.14")
4072             << 6 << 6 << 6
4073             << false << false;
4074
4075     QTest::newRow("mask: after cursor position (middle)")
4076             << ip
4077             << inputMask
4078             << 6 << 6 << 13
4079             << QString("8")
4080             << "192.168.2.18"
4081             << 6 << 6 << 6
4082             << false << false;
4083
4084     QTest::newRow("mask: before selection")
4085             << ip
4086             << inputMask
4087             << 6 << 8 << 0
4088             << QString("125")
4089             << QString("125.168.2.14")
4090             << 6 << 8 << 8
4091             << false << false;
4092
4093     QTest::newRow("mask: before reversed selection")
4094             << ip
4095             << inputMask
4096             << 8 << 6 << 0
4097             << QString("125")
4098             << QString("125.168.2.14")
4099             << 6 << 8 << 6
4100             << false << false;
4101
4102     QTest::newRow("mask: after selection")
4103             << ip
4104             << inputMask
4105             << 6 << 8 << 13
4106             << QString("8")
4107             << "192.168.2.18"
4108             << 6 << 8 << 8
4109             << false << false;
4110
4111     QTest::newRow("mask: after reversed selection")
4112             << ip
4113             << inputMask
4114             << 8 << 6 << 13
4115             << QString("8")
4116             << "192.168.2.18"
4117             << 6 << 8 << 6
4118             << false << false;
4119
4120     QTest::newRow("mask: into selection")
4121             << ip
4122             << inputMask
4123             << 5 << 8 << 6
4124             << QString("75.2")
4125             << QString("192.167.5.24")
4126             << 5 << 8 << 8
4127             << true << false;
4128
4129     QTest::newRow("mask: into reversed selection")
4130             << ip
4131             << inputMask
4132             << 8 << 5 << 6
4133             << QString("75.2")
4134             << QString("192.167.5.24")
4135             << 5 << 8 << 5
4136             << true << false;
4137
4138     QTest::newRow("mask: before start")
4139             << ip
4140             << inputMask
4141             << 0 << 0 << -3
4142             << QString("4")
4143             << ip
4144             << 0 << 0 << 0
4145             << false << false;
4146
4147     QTest::newRow("mask: past end")
4148             << ip
4149             << inputMask
4150             << 0 << 0 << ip.length() + 3
4151             << QString("4")
4152             << ip
4153             << 0 << 0 << 0
4154             << false << false;
4155
4156     QTest::newRow("mask: invalid characters")
4157             << ip
4158             << inputMask
4159             << 0 << 0 << 0
4160             << QString("abc")
4161             << QString("192.168.2.14")
4162             << 0 << 0 << 0
4163             << false << false;
4164
4165     QTest::newRow("mask: mixed validity")
4166             << ip
4167             << inputMask
4168             << 0 << 0 << 0
4169             << QString("a1b2c5")
4170             << QString("125.168.2.14")
4171             << 0 << 0 << 0
4172             << false << false;
4173 }
4174
4175 void tst_qquicktextinput::insert()
4176 {
4177     QFETCH(QString, text);
4178     QFETCH(QString, inputMask);
4179     QFETCH(int, selectionStart);
4180     QFETCH(int, selectionEnd);
4181     QFETCH(int, insertPosition);
4182     QFETCH(QString, insertText);
4183     QFETCH(QString, expectedText);
4184     QFETCH(int, expectedSelectionStart);
4185     QFETCH(int, expectedSelectionEnd);
4186     QFETCH(int, expectedCursorPosition);
4187     QFETCH(bool, selectionChanged);
4188     QFETCH(bool, cursorPositionChanged);
4189
4190     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4191     QQmlComponent textInputComponent(&engine);
4192     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4193     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4194     QVERIFY(textInput != 0);
4195
4196     textInput->select(selectionStart, selectionEnd);
4197
4198     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4199     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4200     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4201     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4202     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4203
4204     textInput->insert(insertPosition, insertText);
4205
4206     QCOMPARE(textInput->text(), expectedText);
4207     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4208
4209     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4210     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4211     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4212
4213     if (selectionStart > selectionEnd)
4214         qSwap(selectionStart, selectionEnd);
4215
4216     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4217     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4218     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4219     QCOMPARE(textSpy.count() > 0, text != expectedText);
4220     QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4221 }
4222
4223 void tst_qquicktextinput::remove_data()
4224 {
4225     QTest::addColumn<QString>("text");
4226     QTest::addColumn<QString>("inputMask");
4227     QTest::addColumn<int>("selectionStart");
4228     QTest::addColumn<int>("selectionEnd");
4229     QTest::addColumn<int>("removeStart");
4230     QTest::addColumn<int>("removeEnd");
4231     QTest::addColumn<QString>("expectedText");
4232     QTest::addColumn<int>("expectedSelectionStart");
4233     QTest::addColumn<int>("expectedSelectionEnd");
4234     QTest::addColumn<int>("expectedCursorPosition");
4235     QTest::addColumn<bool>("selectionChanged");
4236     QTest::addColumn<bool>("cursorPositionChanged");
4237
4238     QTest::newRow("from cursor position (beginning)")
4239             << standard.at(0)
4240             << QString()
4241             << 0 << 0
4242             << 0 << 5
4243             << standard.at(0).mid(5)
4244             << 0 << 0 << 0
4245             << false << false;
4246
4247     QTest::newRow("to cursor position (beginning)")
4248             << standard.at(0)
4249             << QString()
4250             << 0 << 0
4251             << 5 << 0
4252             << standard.at(0).mid(5)
4253             << 0 << 0 << 0
4254             << false << false;
4255
4256     QTest::newRow("to cursor position (end)")
4257             << standard.at(0)
4258             << QString()
4259             << standard.at(0).length() << standard.at(0).length()
4260             << standard.at(0).length() << standard.at(0).length() - 5
4261             << standard.at(0).mid(0, standard.at(0).length() - 5)
4262             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4263             << false << true;
4264
4265     QTest::newRow("to cursor position (end)")
4266             << standard.at(0)
4267             << QString()
4268             << standard.at(0).length() << standard.at(0).length()
4269             << standard.at(0).length() - 5 << standard.at(0).length()
4270             << standard.at(0).mid(0, standard.at(0).length() - 5)
4271             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4272             << false << true;
4273
4274     QTest::newRow("from cursor position (middle)")
4275             << standard.at(0)
4276             << QString()
4277             << 18 << 18
4278             << 18 << 23
4279             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4280             << 18 << 18 << 18
4281             << false << false;
4282
4283     QTest::newRow("to cursor position (middle)")
4284             << standard.at(0)
4285             << QString()
4286             << 23 << 23
4287             << 18 << 23
4288             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4289             << 18 << 18 << 18
4290             << false << true;
4291
4292     QTest::newRow("after cursor position (beginning)")
4293             << standard.at(0)
4294             << QString()
4295             << 0 << 0
4296             << 18 << 23
4297             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4298             << 0 << 0 << 0
4299             << false << false;
4300
4301     QTest::newRow("before cursor position (end)")
4302             << standard.at(0)
4303             << QString()
4304             << standard.at(0).length() << standard.at(0).length()
4305             << 18 << 23
4306             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4307             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
4308             << false << true;
4309
4310     QTest::newRow("before cursor position (middle)")
4311             << standard.at(0)
4312             << QString()
4313             << 23 << 23
4314             << 0 << 5
4315             << standard.at(0).mid(5)
4316             << 18 << 18 << 18
4317             << false << true;
4318
4319     QTest::newRow("after cursor position (middle)")
4320             << standard.at(0)
4321             << QString()
4322             << 18 << 18
4323             << 18 << 23
4324             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4325             << 18 << 18 << 18
4326             << false << false;
4327
4328     QTest::newRow("before selection")
4329             << standard.at(0)
4330             << QString()
4331             << 14 << 19
4332             << 0 << 5
4333             << standard.at(0).mid(5)
4334             << 9 << 14 << 14
4335             << false << true;
4336
4337     QTest::newRow("before reversed selection")
4338             << standard.at(0)
4339             << QString()
4340             << 19 << 14
4341             << 0 << 5
4342             << standard.at(0).mid(5)
4343             << 9 << 14 << 9
4344             << false << true;
4345
4346     QTest::newRow("after selection")
4347             << standard.at(0)
4348             << QString()
4349             << 14 << 19
4350             << standard.at(0).length() - 5 << standard.at(0).length()
4351             << standard.at(0).mid(0, standard.at(0).length() - 5)
4352             << 14 << 19 << 19
4353             << false << false;
4354
4355     QTest::newRow("after reversed selection")
4356             << standard.at(0)
4357             << QString()
4358             << 19 << 14
4359             << standard.at(0).length() - 5 << standard.at(0).length()
4360             << standard.at(0).mid(0, standard.at(0).length() - 5)
4361             << 14 << 19 << 14
4362             << false << false;
4363
4364     QTest::newRow("from selection")
4365             << standard.at(0)
4366             << QString()
4367             << 14 << 24
4368             << 18 << 23
4369             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4370             << 14 << 19 << 19
4371             << true << true;
4372
4373     QTest::newRow("from reversed selection")
4374             << standard.at(0)
4375             << QString()
4376             << 24 << 14
4377             << 18 << 23
4378             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
4379             << 14 << 19 << 14
4380             << true << false;
4381
4382     QTest::newRow("cropped beginning")
4383             << standard.at(0)
4384             << QString()
4385             << 0 << 0
4386             << -3 << 4
4387             << standard.at(0).mid(4)
4388             << 0 << 0 << 0
4389             << false << false;
4390
4391     QTest::newRow("cropped end")
4392             << standard.at(0)
4393             << QString()
4394             << 0 << 0
4395             << 23 << standard.at(0).length() + 8
4396             << standard.at(0).mid(0, 23)
4397             << 0 << 0 << 0
4398             << false << false;
4399
4400     QTest::newRow("cropped beginning and end")
4401             << standard.at(0)
4402             << QString()
4403             << 0 << 0
4404             << -9 << standard.at(0).length() + 4
4405             << QString()
4406             << 0 << 0 << 0
4407             << false << false;
4408
4409     const QString inputMask = "009.009.009.009";
4410     const QString ip = "192.168.2.14";
4411
4412     QTest::newRow("mask: from cursor position")
4413             << ip
4414             << inputMask
4415             << 6 << 6
4416             << 6 << 9
4417             << QString("192.16..14")
4418             << 6 << 6 << 6
4419             << false << false;
4420
4421     QTest::newRow("mask: to cursor position")
4422             << ip
4423             << inputMask
4424             << 6 << 6
4425             << 2 << 6
4426             << QString("19.8.2.14")
4427             << 6 << 6 << 6
4428             << false << false;
4429
4430     QTest::newRow("mask: before cursor position")
4431             << ip
4432             << inputMask
4433             << 6 << 6
4434             << 0 << 2
4435             << QString("2.168.2.14")
4436             << 6 << 6 << 6
4437             << false << false;
4438
4439     QTest::newRow("mask: after cursor position")
4440             << ip
4441             << inputMask
4442             << 6 << 6
4443             << 12 << 16
4444             << QString("192.168.2.")
4445             << 6 << 6 << 6
4446             << false << false;
4447
4448     QTest::newRow("mask: before selection")
4449             << ip
4450             << inputMask
4451             << 6 << 8
4452             << 0 << 2
4453             << QString("2.168.2.14")
4454             << 6 << 8 << 8
4455             << false << false;
4456
4457     QTest::newRow("mask: before reversed selection")
4458             << ip
4459             << inputMask
4460             << 8 << 6
4461             << 0 << 2
4462             << QString("2.168.2.14")
4463             << 6 << 8 << 6
4464             << false << false;
4465
4466     QTest::newRow("mask: after selection")
4467             << ip
4468             << inputMask
4469             << 6 << 8
4470             << 12 << 16
4471             << QString("192.168.2.")
4472             << 6 << 8 << 8
4473             << false << false;
4474
4475     QTest::newRow("mask: after reversed selection")
4476             << ip
4477             << inputMask
4478             << 8 << 6
4479             << 12 << 16
4480             << QString("192.168.2.")
4481             << 6 << 8 << 6
4482             << false << false;
4483
4484     QTest::newRow("mask: from selection")
4485             << ip
4486             << inputMask
4487             << 6 << 13
4488             << 8 << 10
4489             << QString("192.168..14")
4490             << 6 << 13 << 13
4491             << true << false;
4492
4493     QTest::newRow("mask: from reversed selection")
4494             << ip
4495             << inputMask
4496             << 13 << 6
4497             << 8 << 10
4498             << QString("192.168..14")
4499             << 6 << 13 << 6
4500             << true << false;
4501
4502     QTest::newRow("mask: cropped beginning")
4503             << ip
4504             << inputMask
4505             << 0 << 0
4506             << -3 << 4
4507             << QString(".168.2.14")
4508             << 0 << 0 << 0
4509             << false << false;
4510
4511     QTest::newRow("mask: cropped end")
4512             << ip
4513             << inputMask
4514             << 0 << 0
4515             << 13 << 28
4516             << QString("192.168.2.1")
4517             << 0 << 0 << 0
4518             << false << false;
4519
4520     QTest::newRow("mask: cropped beginning and end")
4521             << ip
4522             << inputMask
4523             << 0 << 0
4524             << -9 << 28
4525             << QString("...")
4526             << 0 << 0 << 0
4527             << false << false;
4528 }
4529
4530 void tst_qquicktextinput::remove()
4531 {
4532     QFETCH(QString, text);
4533     QFETCH(QString, inputMask);
4534     QFETCH(int, selectionStart);
4535     QFETCH(int, selectionEnd);
4536     QFETCH(int, removeStart);
4537     QFETCH(int, removeEnd);
4538     QFETCH(QString, expectedText);
4539     QFETCH(int, expectedSelectionStart);
4540     QFETCH(int, expectedSelectionEnd);
4541     QFETCH(int, expectedCursorPosition);
4542     QFETCH(bool, selectionChanged);
4543     QFETCH(bool, cursorPositionChanged);
4544
4545     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
4546     QQmlComponent textInputComponent(&engine);
4547     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4548     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4549     QVERIFY(textInput != 0);
4550
4551     textInput->select(selectionStart, selectionEnd);
4552
4553     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
4554     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
4555     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
4556     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
4557     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
4558
4559     textInput->remove(removeStart, removeEnd);
4560
4561     QCOMPARE(textInput->text(), expectedText);
4562     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
4563
4564     if (selectionStart > selectionEnd)  //
4565         qSwap(selectionStart, selectionEnd);
4566
4567     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
4568     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
4569     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
4570
4571     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4572     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4573     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4574     QCOMPARE(textSpy.count() > 0, text != expectedText);
4575
4576     if (cursorPositionChanged)  //
4577         QVERIFY(cursorPositionSpy.count() > 0);
4578 }
4579
4580 void tst_qquicktextinput::keySequence_data()
4581 {
4582     QTest::addColumn<QString>("text");
4583     QTest::addColumn<QKeySequence>("sequence");
4584     QTest::addColumn<int>("selectionStart");
4585     QTest::addColumn<int>("selectionEnd");
4586     QTest::addColumn<int>("cursorPosition");
4587     QTest::addColumn<QString>("expectedText");
4588     QTest::addColumn<QString>("selectedText");
4589     QTest::addColumn<QQuickTextInput::EchoMode>("echoMode");
4590     QTest::addColumn<Qt::Key>("layoutDirection");
4591
4592     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4593
4594     QTest::newRow("select all")
4595             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4596             << 44 << standard.at(0) << standard.at(0)
4597             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4598     QTest::newRow("select start of line")
4599             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4600             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4601             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4602     QTest::newRow("select start of block")
4603             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4604             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
4605             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4606     QTest::newRow("select end of line")
4607             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4608             << 44 << standard.at(0) << standard.at(0).mid(5)
4609             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4610     QTest::newRow("select end of document") // ### Not handled.
4611             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4612             << 3 << standard.at(0) << QString()
4613             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4614     QTest::newRow("select end of block")
4615             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4616             << 44 << standard.at(0) << standard.at(0).mid(18)
4617             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4618     QTest::newRow("delete end of line")
4619             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4620             << 24 << standard.at(0).mid(0, 24) << QString()
4621             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4622     QTest::newRow("move to start of line")
4623             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4624             << 0 << standard.at(0) << QString()
4625             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4626     QTest::newRow("move to start of block")
4627             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4628             << 0 << standard.at(0) << QString()
4629             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4630     QTest::newRow("move to next char")
4631             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4632             << 13 << standard.at(0) << QString()
4633             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4634     QTest::newRow("move to previous char (ltr)")
4635             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4636             << 2 << standard.at(0) << QString()
4637             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4638     QTest::newRow("move to previous char (rtl)")
4639             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4640             << 4 << standard.at(0) << QString()
4641             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4642     QTest::newRow("move to previous char with selection")
4643             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4644             << 3 << standard.at(0) << QString()
4645             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4646     QTest::newRow("select next char (ltr)")
4647             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4648             << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4649             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4650     QTest::newRow("select next char (rtl)")
4651             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4652             << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4653             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4654     QTest::newRow("select previous char (ltr)")
4655             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4656             << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4657             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4658     QTest::newRow("select previous char (rtl)")
4659             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4660             << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4661             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4662     QTest::newRow("move to next word (ltr)")
4663             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4664             << 10 << standard.at(0) << QString()
4665             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4666     QTest::newRow("move to next word (rtl)")
4667             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4668             << 4 << standard.at(0) << QString()
4669             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4670     QTest::newRow("move to next word (password,ltr)")
4671             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4672             << 44 << standard.at(0) << QString()
4673             << QQuickTextInput::Password << Qt::Key_Direction_L;
4674     QTest::newRow("move to next word (password,rtl)")
4675             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4676             << 0 << standard.at(0) << QString()
4677             << QQuickTextInput::Password << Qt::Key_Direction_R;
4678     QTest::newRow("move to previous word (ltr)")
4679             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4680             << 4 << standard.at(0) << QString()
4681             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4682     QTest::newRow("move to previous word (rlt)")
4683             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4684             << 10 << standard.at(0) << QString()
4685             << QQuickTextInput::Normal << Qt::Key_Direction_R;
4686     QTest::newRow("move to previous word (password,ltr)")
4687             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4688             << 0 << standard.at(0) << QString()
4689             << QQuickTextInput::Password << Qt::Key_Direction_L;
4690     QTest::newRow("move to previous word (password,rtl)")
4691             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4692             << 44 << standard.at(0) << QString()
4693             << QQuickTextInput::Password << Qt::Key_Direction_R;
4694     QTest::newRow("select next word")
4695             << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4696             << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4697             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4698     QTest::newRow("select previous word")
4699             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4700             << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4701             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4702     QTest::newRow("delete (selection)")
4703             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4704             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4705             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4706     QTest::newRow("delete (no selection)")
4707             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4708             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4709             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4710     QTest::newRow("delete end of word")
4711             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4712             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4713             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4714     QTest::newRow("delete start of word")
4715             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4716             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4717             << QQuickTextInput::Normal << Qt::Key_Direction_L;
4718 }
4719
4720 void tst_qquicktextinput::keySequence()
4721 {
4722     QFETCH(QString, text);
4723     QFETCH(QKeySequence, sequence);
4724     QFETCH(int, selectionStart);
4725     QFETCH(int, selectionEnd);
4726     QFETCH(int, cursorPosition);
4727     QFETCH(QString, expectedText);
4728     QFETCH(QString, selectedText);
4729     QFETCH(QQuickTextInput::EchoMode, echoMode);
4730     QFETCH(Qt::Key, layoutDirection);
4731
4732     if (sequence.isEmpty()) {
4733         QSKIP("Key sequence is undefined");
4734     }
4735
4736     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4737     QQmlComponent textInputComponent(&engine);
4738     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4739     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4740     QVERIFY(textInput != 0);
4741     textInput->setEchoMode(echoMode);
4742
4743     QQuickCanvas canvas;
4744     textInput->setParentItem(canvas.rootItem());
4745     canvas.show();
4746     canvas.requestActivateWindow();
4747     QTest::qWaitForWindowShown(&canvas);
4748     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4749
4750     simulateKey(&canvas, layoutDirection);
4751
4752     textInput->select(selectionStart, selectionEnd);
4753
4754     simulateKeys(&canvas, sequence);
4755
4756     QCOMPARE(textInput->cursorPosition(), cursorPosition);
4757     QCOMPARE(textInput->text(), expectedText);
4758     QCOMPARE(textInput->selectedText(), selectedText);
4759 }
4760
4761 #define NORMAL 0
4762 #define REPLACE_UNTIL_END 1
4763
4764 void tst_qquicktextinput::undo_data()
4765 {
4766     QTest::addColumn<QStringList>("insertString");
4767     QTest::addColumn<IntList>("insertIndex");
4768     QTest::addColumn<IntList>("insertMode");
4769     QTest::addColumn<QStringList>("expectedString");
4770     QTest::addColumn<bool>("use_keys");
4771
4772     for (int i=0; i<2; i++) {
4773         QString keys_str = "keyboard";
4774         bool use_keys = true;
4775         if (i==0) {
4776             keys_str = "insert";
4777             use_keys = false;
4778         }
4779
4780         {
4781             IntList insertIndex;
4782             IntList insertMode;
4783             QStringList insertString;
4784             QStringList expectedString;
4785
4786             insertIndex << -1;
4787             insertMode << NORMAL;
4788             insertString << "1";
4789
4790             insertIndex << -1;
4791             insertMode << NORMAL;
4792             insertString << "5";
4793
4794             insertIndex << 1;
4795             insertMode << NORMAL;
4796             insertString << "3";
4797
4798             insertIndex << 1;
4799             insertMode << NORMAL;
4800             insertString << "2";
4801
4802             insertIndex << 3;
4803             insertMode << NORMAL;
4804             insertString << "4";
4805
4806             expectedString << "12345";
4807             expectedString << "1235";
4808             expectedString << "135";
4809             expectedString << "15";
4810             expectedString << "";
4811
4812             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4813                 insertString <<
4814                 insertIndex <<
4815                 insertMode <<
4816                 expectedString <<
4817                 bool(use_keys);
4818         }
4819         {
4820             IntList insertIndex;
4821             IntList insertMode;
4822             QStringList insertString;
4823             QStringList expectedString;
4824
4825             insertIndex << -1;
4826             insertMode << NORMAL;
4827             insertString << "World"; // World
4828
4829             insertIndex << 0;
4830             insertMode << NORMAL;
4831             insertString << "Hello"; // HelloWorld
4832
4833             insertIndex << 0;
4834             insertMode << NORMAL;
4835             insertString << "Well"; // WellHelloWorld
4836
4837             insertIndex << 9;
4838             insertMode << NORMAL;
4839             insertString << "There"; // WellHelloThereWorld;
4840
4841             expectedString << "WellHelloThereWorld";
4842             expectedString << "WellHelloWorld";
4843             expectedString << "HelloWorld";
4844             expectedString << "World";
4845             expectedString << "";
4846
4847             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4848                 insertString <<
4849                 insertIndex <<
4850                 insertMode <<
4851                 expectedString <<
4852                 bool(use_keys);
4853         }
4854         {
4855             IntList insertIndex;
4856             IntList insertMode;
4857             QStringList insertString;
4858             QStringList expectedString;
4859
4860             insertIndex << -1;
4861             insertMode << NORMAL;
4862             insertString << "Ensuring";
4863
4864             insertIndex << -1;
4865             insertMode << NORMAL;
4866             insertString << " instan";
4867
4868             insertIndex << 9;
4869             insertMode << NORMAL;
4870             insertString << "an ";
4871
4872             insertIndex << 10;
4873             insertMode << REPLACE_UNTIL_END;
4874             insertString << " unique instance.";
4875
4876             expectedString << "Ensuring a unique instance.";
4877             expectedString << "Ensuring an instan";
4878             expectedString << "Ensuring instan";
4879             expectedString << "";
4880
4881             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4882                 insertString <<
4883                 insertIndex <<
4884                 insertMode <<
4885                 expectedString <<
4886                 bool(use_keys);
4887         }
4888     }
4889 }
4890
4891 void tst_qquicktextinput::undo()
4892 {
4893     QFETCH(QStringList, insertString);
4894     QFETCH(IntList, insertIndex);
4895     QFETCH(IntList, insertMode);
4896     QFETCH(QStringList, expectedString);
4897
4898     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4899     QQmlComponent textInputComponent(&engine);
4900     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4901     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4902     QVERIFY(textInput != 0);
4903
4904     QQuickCanvas canvas;
4905     textInput->setParentItem(canvas.rootItem());
4906     canvas.show();
4907     canvas.requestActivateWindow();
4908     QTest::qWaitForWindowShown(&canvas);
4909     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4910
4911     QVERIFY(!textInput->canUndo());
4912
4913     QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4914
4915     int i;
4916
4917 // STEP 1: First build up an undo history by inserting or typing some strings...
4918     for (i = 0; i < insertString.size(); ++i) {
4919         if (insertIndex[i] > -1)
4920             textInput->setCursorPosition(insertIndex[i]);
4921
4922  // experimental stuff
4923         if (insertMode[i] == REPLACE_UNTIL_END) {
4924             textInput->select(insertIndex[i], insertIndex[i] + 8);
4925
4926             // This is what I actually want...
4927             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4928         }
4929
4930         for (int j = 0; j < insertString.at(i).length(); j++)
4931             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4932     }
4933
4934     QCOMPARE(spy.count(), 1);
4935
4936 // STEP 2: Next call undo several times and see if we can restore to the previous state
4937     for (i = 0; i < expectedString.size() - 1; ++i) {
4938         QCOMPARE(textInput->text(), expectedString[i]);
4939         QVERIFY(textInput->canUndo());
4940         textInput->undo();
4941     }
4942
4943 // STEP 3: Verify that we have undone everything
4944     QVERIFY(textInput->text().isEmpty());
4945     QVERIFY(!textInput->canUndo());
4946     QCOMPARE(spy.count(), 2);
4947 }
4948
4949 void tst_qquicktextinput::redo_data()
4950 {
4951     QTest::addColumn<QStringList>("insertString");
4952     QTest::addColumn<IntList>("insertIndex");
4953     QTest::addColumn<QStringList>("expectedString");
4954
4955     {
4956         IntList insertIndex;
4957         QStringList insertString;
4958         QStringList expectedString;
4959
4960         insertIndex << -1;
4961         insertString << "World"; // World
4962         insertIndex << 0;
4963         insertString << "Hello"; // HelloWorld
4964         insertIndex << 0;
4965         insertString << "Well"; // WellHelloWorld
4966         insertIndex << 9;
4967         insertString << "There"; // WellHelloThereWorld;
4968
4969         expectedString << "World";
4970         expectedString << "HelloWorld";
4971         expectedString << "WellHelloWorld";
4972         expectedString << "WellHelloThereWorld";
4973
4974         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4975     }
4976 }
4977
4978 void tst_qquicktextinput::redo()
4979 {
4980     QFETCH(QStringList, insertString);
4981     QFETCH(IntList, insertIndex);
4982     QFETCH(QStringList, expectedString);
4983
4984     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4985     QQmlComponent textInputComponent(&engine);
4986     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4987     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4988     QVERIFY(textInput != 0);
4989
4990     QQuickCanvas canvas;
4991     textInput->setParentItem(canvas.rootItem());
4992     canvas.show();
4993     canvas.requestActivateWindow();
4994     QTest::qWaitForWindowShown(&canvas);
4995     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4996
4997     QVERIFY(!textInput->canUndo());
4998     QVERIFY(!textInput->canRedo());
4999
5000     QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
5001
5002     int i;
5003     // inserts the diff strings at diff positions
5004     for (i = 0; i < insertString.size(); ++i) {
5005         if (insertIndex[i] > -1)
5006             textInput->setCursorPosition(insertIndex[i]);
5007         for (int j = 0; j < insertString.at(i).length(); j++)
5008             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
5009         QVERIFY(textInput->canUndo());
5010         QVERIFY(!textInput->canRedo());
5011     }
5012
5013     QCOMPARE(spy.count(), 0);
5014
5015     // undo everything
5016     while (!textInput->text().isEmpty()) {
5017         QVERIFY(textInput->canUndo());
5018         textInput->undo();
5019         QVERIFY(textInput->canRedo());
5020     }
5021
5022     QCOMPARE(spy.count(), 1);
5023
5024     for (i = 0; i < expectedString.size(); ++i) {
5025         QVERIFY(textInput->canRedo());
5026         textInput->redo();
5027         QCOMPARE(textInput->text() , expectedString[i]);
5028         QVERIFY(textInput->canUndo());
5029     }
5030     QVERIFY(!textInput->canRedo());
5031     QCOMPARE(spy.count(), 2);
5032 }
5033
5034 void tst_qquicktextinput::undo_keypressevents_data()
5035 {
5036     QTest::addColumn<KeyList>("keys");
5037     QTest::addColumn<QStringList>("expectedString");
5038
5039     {
5040         KeyList keys;
5041         QStringList expectedString;
5042
5043         keys << "AFRAID"
5044                 << QKeySequence::MoveToStartOfLine
5045                 << "VERY"
5046                 << Qt::Key_Left
5047                 << Qt::Key_Left
5048                 << Qt::Key_Left
5049                 << Qt::Key_Left
5050                 << "BE"
5051                 << QKeySequence::MoveToEndOfLine
5052                 << "!";
5053
5054         expectedString << "BEVERYAFRAID!";
5055         expectedString << "BEVERYAFRAID";
5056         expectedString << "VERYAFRAID";
5057         expectedString << "AFRAID";
5058
5059         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
5060     } {
5061         KeyList keys;
5062         QStringList expectedString;
5063
5064         // inserting '1234'
5065         keys << "1234" << QKeySequence::MoveToStartOfLine
5066                 // skipping '12'
5067                 << Qt::Key_Right << Qt::Key_Right
5068                 // selecting '34'
5069                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5070                 // deleting '34'
5071                 << Qt::Key_Delete;
5072
5073         expectedString << "12";
5074         expectedString << "1234";
5075
5076         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
5077     } {
5078         KeyList keys;
5079         QStringList expectedString;
5080
5081         // inserting 'AB12'
5082         keys << "AB12"
5083                 << QKeySequence::MoveToStartOfLine
5084                 // selecting 'AB'
5085                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5086                 << Qt::Key_Delete
5087                 << QKeySequence::Undo
5088                 << Qt::Key_Right
5089                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5090                 << Qt::Key_Delete;
5091
5092         expectedString << "AB";
5093         expectedString << "AB12";
5094
5095         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
5096     } {
5097         KeyList keys;
5098         QStringList expectedString;
5099
5100         // inserting 'ABCD'
5101         keys << "abcd"
5102                 //move left two
5103                 << Qt::Key_Left << Qt::Key_Left
5104                 // inserting '1234'
5105                 << "1234"
5106                 // selecting '1234'
5107                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5108                 // overwriting '1234' with '5'
5109                 << "5"
5110                 // undoing deletion of 'AB'
5111                 << QKeySequence::Undo
5112                 // overwriting '1234' with '6'
5113                 << "6";
5114
5115         expectedString << "ab6cd";
5116         // for versions previous to 3.2 we overwrite needed two undo operations
5117         expectedString << "ab1234cd";
5118         expectedString << "abcd";
5119
5120         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5121     } {
5122         KeyList keys;
5123         QStringList expectedString;
5124
5125         // inserting 'ABC'
5126         keys << "ABC"
5127                 // removes 'C'
5128                 << Qt::Key_Backspace;
5129
5130         expectedString << "AB";
5131         expectedString << "ABC";
5132
5133         QTest::newRow("Inserts,backspace") << keys << expectedString;
5134     } {
5135         KeyList keys;
5136         QStringList expectedString;
5137
5138         keys << "ABC"
5139                 // removes 'C'
5140                 << Qt::Key_Backspace
5141                 // inserting 'Z'
5142                 << "Z";
5143
5144         expectedString << "ABZ";
5145         expectedString << "AB";
5146         expectedString << "ABC";
5147
5148         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
5149     } {
5150         KeyList keys;
5151         QStringList expectedString;
5152
5153         // inserting '123'
5154         keys << "123" << QKeySequence::MoveToStartOfLine
5155             // selecting '123'
5156              << QKeySequence::SelectEndOfLine
5157             // overwriting '123' with 'ABC'
5158              << "ABC";
5159
5160         expectedString << "ABC";
5161         // for versions previous to 3.2 we overwrite needed two undo operations
5162         expectedString << "123";
5163
5164         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
5165     } {
5166         KeyList keys;
5167         QStringList expectedString;
5168
5169         // inserting '123'
5170         keys << "123"
5171                 << QKeySequence::Undo
5172                 << QKeySequence::Redo;
5173
5174         expectedString << "123";
5175         expectedString << QString();
5176
5177         QTest::newRow("Insert,undo,redo") << keys << expectedString;
5178     }
5179 }
5180
5181 void tst_qquicktextinput::undo_keypressevents()
5182 {
5183     QFETCH(KeyList, keys);
5184     QFETCH(QStringList, expectedString);
5185
5186     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
5187     QQmlComponent textInputComponent(&engine);
5188     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5189     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5190     QVERIFY(textInput != 0);
5191
5192     QQuickCanvas canvas;
5193     textInput->setParentItem(canvas.rootItem());
5194     canvas.show();
5195     canvas.requestActivateWindow();
5196     QTest::qWaitForWindowShown(&canvas);
5197     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5198
5199     simulateKeys(&canvas, keys);
5200
5201     for (int i = 0; i < expectedString.size(); ++i) {
5202         QCOMPARE(textInput->text() , expectedString[i]);
5203         textInput->undo();
5204     }
5205     QVERIFY(textInput->text().isEmpty());
5206 }
5207
5208 void tst_qquicktextinput::QTBUG_19956()
5209 {
5210     QFETCH(QString, url);
5211
5212     QQuickView canvas(testFileUrl(url));
5213     canvas.show();
5214     canvas.requestActivateWindow();
5215     QTest::qWaitForWindowShown(&canvas);
5216     QVERIFY(canvas.rootObject() != 0);
5217     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5218     QVERIFY(input);
5219     input->setFocus(true);
5220     QVERIFY(input->hasActiveFocus());
5221
5222     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
5223     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5224     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
5225     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5226
5227     canvas.rootObject()->setProperty("topvalue", 15);
5228     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
5229     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5230
5231     canvas.rootObject()->setProperty("topvalue", 25);
5232     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
5233     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5234
5235     canvas.rootObject()->setProperty("bottomvalue", 21);
5236     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
5237     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5238
5239     canvas.rootObject()->setProperty("bottomvalue", 10);
5240     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
5241     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5242 }
5243
5244 void tst_qquicktextinput::QTBUG_19956_regexp()
5245 {
5246     QUrl url = testFileUrl("qtbug-19956regexp.qml");
5247
5248     QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
5249     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
5250
5251     QQuickView canvas(url);
5252     canvas.show();
5253     canvas.requestActivateWindow();
5254     QTest::qWaitForWindowShown(&canvas);
5255     QVERIFY(canvas.rootObject() != 0);
5256     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
5257     QVERIFY(input);
5258     input->setFocus(true);
5259     QVERIFY(input->hasActiveFocus());
5260
5261     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5262     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5263     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
5264     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5265
5266     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
5267     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
5268     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
5269
5270     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
5271     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
5272     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
5273 }
5274
5275 void tst_qquicktextinput::implicitSize_data()
5276 {
5277     QTest::addColumn<QString>("text");
5278     QTest::addColumn<QString>("wrap");
5279     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextInput.NoWrap";
5280     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextInput.Wrap";
5281 }
5282
5283 void tst_qquicktextinput::implicitSize()
5284 {
5285     QFETCH(QString, text);
5286     QFETCH(QString, wrap);
5287     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
5288     QQmlComponent textComponent(&engine);
5289     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5290     QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
5291
5292     QVERIFY(textObject->width() < textObject->implicitWidth());
5293     QVERIFY(textObject->height() == textObject->implicitHeight());
5294
5295     textObject->resetWidth();
5296     QVERIFY(textObject->width() == textObject->implicitWidth());
5297     QVERIFY(textObject->height() == textObject->implicitHeight());
5298 }
5299
5300 void tst_qquicktextinput::implicitSizeBinding_data()
5301 {
5302     implicitSize_data();
5303 }
5304
5305 void tst_qquicktextinput::implicitSizeBinding()
5306 {
5307     QFETCH(QString, text);
5308     QFETCH(QString, wrap);
5309     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }";
5310     QQmlComponent textComponent(&engine);
5311     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
5312     QScopedPointer<QObject> object(textComponent.create());
5313     QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data());
5314
5315     QCOMPARE(textObject->width(), textObject->implicitWidth());
5316     QCOMPARE(textObject->height(), textObject->implicitHeight());
5317
5318     textObject->resetWidth();
5319     QCOMPARE(textObject->width(), textObject->implicitWidth());
5320     QCOMPARE(textObject->height(), textObject->implicitHeight());
5321
5322     textObject->resetHeight();
5323     QCOMPARE(textObject->width(), textObject->implicitWidth());
5324     QCOMPARE(textObject->height(), textObject->implicitHeight());
5325 }
5326
5327
5328 void tst_qquicktextinput::negativeDimensions()
5329 {
5330     // Verify this doesn't assert during initialization.
5331     QQmlComponent component(&engine, testFileUrl("negativeDimensions.qml"));
5332     QScopedPointer<QObject> o(component.create());
5333     QVERIFY(o);
5334     QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
5335     QVERIFY(input);
5336     QCOMPARE(input->width(), qreal(-1));
5337     QCOMPARE(input->height(), qreal(-1));
5338 }
5339
5340
5341 void tst_qquicktextinput::setInputMask_data()
5342 {
5343     QTest::addColumn<QString>("mask");
5344     QTest::addColumn<QString>("input");
5345     QTest::addColumn<QString>("expectedText");
5346     QTest::addColumn<QString>("expectedDisplay");
5347     QTest::addColumn<bool>("insert_text");
5348
5349     // both keyboard and insert()
5350     for (int i=0; i<2; i++) {
5351         bool insert_text = i==0 ? false : true;
5352         QString insert_mode = "keys ";
5353         if (insert_text)
5354             insert_mode = "insert ";
5355
5356         QTest::newRow(QString(insert_mode + "ip_localhost").toLatin1())
5357             << QString("000.000.000.000")
5358             << QString("127.0.0.1")
5359             << QString("127.0.0.1")
5360             << QString("127.0  .0  .1  ")
5361             << bool(insert_text);
5362         QTest::newRow(QString(insert_mode + "mac").toLatin1())
5363             << QString("HH:HH:HH:HH:HH:HH;#")
5364             << QString("00:E0:81:21:9E:8E")
5365             << QString("00:E0:81:21:9E:8E")
5366             << QString("00:E0:81:21:9E:8E")
5367             << bool(insert_text);
5368         QTest::newRow(QString(insert_mode + "mac2").toLatin1())
5369             << QString("<HH:>HH:!HH:HH:HH:HH;#")
5370             << QString("AAe081219E8E")
5371             << QString("aa:E0:81:21:9E:8E")
5372             << QString("aa:E0:81:21:9E:8E")
5373             << bool(insert_text);
5374         QTest::newRow(QString(insert_mode + "byte").toLatin1())
5375             << QString("BBBBBBBB;0")
5376             << QString("11011001")
5377             << QString("11111")
5378             << QString("11011001")
5379             << bool(insert_text);
5380         QTest::newRow(QString(insert_mode + "halfbytes").toLatin1())
5381             << QString("bbbb.bbbb;-")
5382             << QString("110. 0001")
5383             << QString("110.0001")
5384             << QString("110-.0001")
5385             << bool(insert_text);
5386         QTest::newRow(QString(insert_mode + "blank char same type as content").toLatin1())
5387             << QString("000.000.000.000;0")
5388             << QString("127.0.0.1")
5389             << QString("127...1")
5390             << QString("127.000.000.100")
5391             << bool(insert_text);
5392         QTest::newRow(QString(insert_mode + "parts of ip_localhost").toLatin1())
5393             << QString("000.000.000.000")
5394             << QString(".0.0.1")
5395             << QString(".0.0.1")
5396             << QString("   .0  .0  .1  ")
5397             << bool(insert_text);
5398         QTest::newRow(QString(insert_mode + "ip_null").toLatin1())
5399             << QString("000.000.000.000")
5400             << QString()
5401             << QString("...")
5402             << QString("   .   .   .   ")
5403             << bool(insert_text);
5404         QTest::newRow(QString(insert_mode + "ip_null_hash").toLatin1())
5405             << QString("000.000.000.000;#")
5406             << QString()
5407             << QString("...")
5408             << QString("###.###.###.###")
5409             << bool(insert_text);
5410         QTest::newRow(QString(insert_mode + "ip_overflow").toLatin1())
5411             << QString("000.000.000.000")
5412             << QString("1234123412341234")
5413             << QString("123.412.341.234")
5414             << QString("123.412.341.234")
5415             << bool(insert_text);
5416         QTest::newRow(QString(insert_mode + "uppercase").toLatin1())
5417             << QString(">AAAA")
5418             << QString("AbCd")
5419             << QString("ABCD")
5420             << QString("ABCD")
5421             << bool(insert_text);
5422         QTest::newRow(QString(insert_mode + "lowercase").toLatin1())
5423             << QString("<AAAA")
5424             << QString("AbCd")
5425             << QString("abcd")
5426             << QString("abcd")
5427             << bool(insert_text);
5428
5429         QTest::newRow(QString(insert_mode + "nocase").toLatin1())
5430             << QString("!AAAA")
5431             << QString("AbCd")
5432             << QString("AbCd")
5433             << QString("AbCd")
5434             << bool(insert_text);
5435         QTest::newRow(QString(insert_mode + "nocase1").toLatin1())
5436             << QString("!A!A!A!A")
5437             << QString("AbCd")
5438             << QString("AbCd")
5439             << QString("AbCd")
5440             << bool(insert_text);
5441         QTest::newRow(QString(insert_mode + "nocase2").toLatin1())
5442             << QString("AAAA")
5443             << QString("AbCd")
5444             << QString("AbCd")
5445             << QString("AbCd")
5446             << bool(insert_text);
5447
5448         QTest::newRow(QString(insert_mode + "reserved").toLatin1())
5449             << QString("{n}[0]")
5450             << QString("A9")
5451             << QString("A9")
5452             << QString("A9")
5453             << bool(insert_text);
5454         QTest::newRow(QString(insert_mode + "escape01").toLatin1())
5455             << QString("\\\\N\\\\n00")
5456             << QString("9")
5457             << QString("Nn9")
5458             << QString("Nn9 ")
5459             << bool(insert_text);
5460         QTest::newRow(QString(insert_mode + "escape02").toLatin1())
5461             << QString("\\\\\\\\00")
5462             << QString("0")
5463             << QString("\\0")
5464             << QString("\\0 ")
5465             << bool(insert_text);
5466         QTest::newRow(QString(insert_mode + "escape03").toLatin1())
5467             << QString("\\\\(00\\\\)")
5468             << QString("0")
5469             << QString("(0)")
5470             << QString("(0 )")
5471             << bool(insert_text);
5472
5473         QTest::newRow(QString(insert_mode + "upper_lower_nocase1").toLatin1())
5474             << QString(">AAAA<AAAA!AAAA")
5475             << QString("AbCdEfGhIjKl")
5476             << QString("ABCDefghIjKl")
5477             << QString("ABCDefghIjKl")
5478             << bool(insert_text);
5479         QTest::newRow(QString(insert_mode + "upper_lower_nocase2").toLatin1())
5480             << QString(">aaaa<aaaa!aaaa")
5481             << QString("AbCdEfGhIjKl")
5482             << QString("ABCDefghIjKl")
5483             << QString("ABCDefghIjKl")
5484             << bool(insert_text);
5485
5486         QTest::newRow(QString(insert_mode + "exact_case1").toLatin1())
5487             << QString(">A<A<A>A>A<A!A!A")
5488             << QString("AbCdEFGH")
5489             << QString("AbcDEfGH")
5490             << QString("AbcDEfGH")
5491             << bool(insert_text);
5492         QTest::newRow(QString(insert_mode + "exact_case2").toLatin1())
5493             << QString(">A<A<A>A>A<A!A!A")
5494             << QString("aBcDefgh")
5495             << QString("AbcDEfgh")
5496             << QString("AbcDEfgh")
5497             << bool(insert_text);
5498         QTest::newRow(QString(insert_mode + "exact_case3").toLatin1())
5499             << QString(">a<a<a>a>a<a!a!a")
5500             << QString("AbCdEFGH")
5501             << QString("AbcDEfGH")
5502             << QString("AbcDEfGH")
5503             << bool(insert_text);
5504         QTest::newRow(QString(insert_mode + "exact_case4").toLatin1())
5505             << QString(">a<a<a>a>a<a!a!a")
5506             << QString("aBcDefgh")
5507             << QString("AbcDEfgh")
5508             << QString("AbcDEfgh")
5509             << bool(insert_text);
5510         QTest::newRow(QString(insert_mode + "exact_case5").toLatin1())
5511             << QString(">H<H<H>H>H<H!H!H")
5512             << QString("aBcDef01")
5513             << QString("AbcDEf01")
5514             << QString("AbcDEf01")
5515             << bool(insert_text);
5516         QTest::newRow(QString(insert_mode + "exact_case6").toLatin1())
5517             << QString(">h<h<h>h>h<h!h!h")
5518             << QString("aBcDef92")
5519             << QString("AbcDEf92")
5520             << QString("AbcDEf92")
5521             << bool(insert_text);
5522
5523         QTest::newRow(QString(insert_mode + "illegal_keys1").toLatin1())
5524             << QString("AAAAAAAA")
5525             << QString("A2#a;.0!")
5526             << QString("Aa")
5527             << QString("Aa      ")
5528             << bool(insert_text);
5529         QTest::newRow(QString(insert_mode + "illegal_keys2").toLatin1())
5530             << QString("AAAA")
5531             << QString("f4f4f4f4")
5532             << QString("ffff")
5533             << QString("ffff")
5534             << bool(insert_text);
5535         QTest::newRow(QString(insert_mode + "blank=input").toLatin1())
5536             << QString("9999;0")
5537             << QString("2004")
5538             << QString("2004")
5539             << QString("2004")
5540             << bool(insert_text);
5541     }
5542 }
5543
5544 void tst_qquicktextinput::setInputMask()
5545 {
5546     QFETCH(QString, mask);
5547     QFETCH(QString, input);
5548     QFETCH(QString, expectedText);
5549     QFETCH(QString, expectedDisplay);
5550     QFETCH(bool, insert_text);
5551
5552     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5553     QQmlComponent textInputComponent(&engine);
5554     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5555     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5556     QVERIFY(textInput != 0);
5557
5558     // then either insert using insert() or keyboard
5559     if (insert_text) {
5560         textInput->insert(0, input);
5561     } else {
5562         QQuickCanvas canvas;
5563         textInput->setParentItem(canvas.rootItem());
5564         canvas.show();
5565         canvas.requestActivateWindow();
5566         QTest::qWaitForWindowShown(&canvas);
5567         QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5568
5569         simulateKey(&canvas, Qt::Key_Home);
5570         for (int i = 0; i < input.length(); i++)
5571             QTest::keyClick(&canvas, input.at(i).toLatin1());
5572     }
5573
5574     QEXPECT_FAIL( "keys blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5575     QEXPECT_FAIL( "insert blank=input", "To eat blanks or not? Known issue. Task 43172", Abort);
5576
5577     QCOMPARE(textInput->text(), expectedText);
5578     QCOMPARE(textInput->displayText(), expectedDisplay);
5579 }
5580
5581 void tst_qquicktextinput::inputMask_data()
5582 {
5583     QTest::addColumn<QString>("mask");
5584     QTest::addColumn<QString>("expectedMask");
5585
5586     // if no mask is set a nul string should be returned
5587     QTest::newRow("nul 1") << QString("") << QString();
5588     QTest::newRow("nul 2") << QString() << QString();
5589
5590     // try different masks
5591     QTest::newRow("mask 1") << QString("000.000.000.000") << QString("000.000.000.000; ");
5592     QTest::newRow("mask 2") << QString("000.000.000.000;#") << QString("000.000.000.000;#");
5593     QTest::newRow("mask 3") << QString("AAA.aa.999.###;") << QString("AAA.aa.999.###; ");
5594     QTest::newRow("mask 4") << QString(">abcdef<GHIJK") << QString(">abcdef<GHIJK; ");
5595
5596     // set an invalid input mask...
5597     // the current behaviour is that this exact (faulty) string is returned.
5598     QTest::newRow("invalid") << QString("ABCDEFGHIKLMNOP;") << QString("ABCDEFGHIKLMNOP; ");
5599
5600     // verify that we can unset the mask again
5601     QTest::newRow("unset") << QString("") << QString();
5602 }
5603
5604 void tst_qquicktextinput::inputMask()
5605 {
5606     QFETCH(QString, mask);
5607     QFETCH(QString, expectedMask);
5608
5609     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5610     QQmlComponent textInputComponent(&engine);
5611     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5612     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5613     QVERIFY(textInput != 0);
5614
5615     QCOMPARE(textInput->inputMask(), expectedMask);
5616 }
5617
5618 void tst_qquicktextinput::clearInputMask()
5619 {
5620     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"000.000.000.000\" }";
5621     QQmlComponent textInputComponent(&engine);
5622     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5623     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5624     QVERIFY(textInput != 0);
5625
5626     QVERIFY(textInput->inputMask() != QString());
5627     textInput->setInputMask(QString());
5628     QCOMPARE(textInput->inputMask(), QString());
5629 }
5630
5631 void tst_qquicktextinput::keypress_inputMask_data()
5632 {
5633     QTest::addColumn<QString>("mask");
5634     QTest::addColumn<KeyList>("keys");
5635     QTest::addColumn<QString>("expectedText");
5636     QTest::addColumn<QString>("expectedDisplayText");
5637
5638     {
5639         KeyList keys;
5640         // inserting 'A1.2B'
5641         keys << Qt::Key_Home << "A1.2B";
5642         QTest::newRow("jumping on period(separator)") << QString("000.000;_") << keys << QString("1.2") << QString("1__.2__");
5643     }
5644     {
5645         KeyList keys;
5646         // inserting '0!P3'
5647         keys << Qt::Key_Home << "0!P3";
5648         QTest::newRow("jumping on input") << QString("D0.AA.XX.AA.00;_") << keys << QString("0..!P..3") << QString("_0.__.!P.__.3_");
5649     }
5650     {
5651         KeyList keys;
5652         // pressing delete
5653         keys << Qt::Key_Home
5654              << Qt::Key_Delete;
5655         QTest::newRow("delete") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5656     }
5657     {
5658         KeyList keys;
5659         // selecting all and delete
5660         keys << Qt::Key_Home
5661              << Key(Qt::ShiftModifier, Qt::Key_End)
5662              << Qt::Key_Delete;
5663         QTest::newRow("deleting all") << QString("000.000;_") << keys << QString(".") << QString("___.___");
5664     }
5665     {
5666         KeyList keys;
5667         // inserting '12.12' then two backspaces
5668         keys << Qt::Key_Home << "12.12" << Qt::Key_Backspace << Qt::Key_Backspace;
5669         QTest::newRow("backspace") << QString("000.000;_") << keys << QString("12.") << QString("12_.___");
5670     }
5671     {
5672         KeyList keys;
5673         // inserting '12ab'
5674         keys << Qt::Key_Home << "12ab";
5675         QTest::newRow("uppercase") << QString("9999 >AA;_") << keys << QString("12 AB") << QString("12__ AB");
5676     }
5677 }
5678
5679 void tst_qquicktextinput::keypress_inputMask()
5680 {
5681     QFETCH(QString, mask);
5682     QFETCH(KeyList, keys);
5683     QFETCH(QString, expectedText);
5684     QFETCH(QString, expectedDisplayText);
5685
5686     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5687     QQmlComponent textInputComponent(&engine);
5688     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5689     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5690     QVERIFY(textInput != 0);
5691
5692     QQuickCanvas canvas;
5693     textInput->setParentItem(canvas.rootItem());
5694     canvas.show();
5695     canvas.requestActivateWindow();
5696     QTest::qWaitForWindowShown(&canvas);
5697     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5698
5699     simulateKeys(&canvas, keys);
5700
5701     QCOMPARE(textInput->text(), expectedText);
5702     QCOMPARE(textInput->displayText(), expectedDisplayText);
5703 }
5704
5705
5706 void tst_qquicktextinput::hasAcceptableInputMask_data()
5707 {
5708     QTest::addColumn<QString>("optionalMask");
5709     QTest::addColumn<QString>("requiredMask");
5710     QTest::addColumn<QString>("invalid");
5711     QTest::addColumn<QString>("valid");
5712
5713     QTest::newRow("Alphabetic optional and required")
5714         << QString("aaaa") << QString("AAAA") << QString("ab") << QString("abcd");
5715     QTest::newRow("Alphanumeric optional and require")
5716         << QString("nnnn") << QString("NNNN") << QString("R2") << QString("R2D2");
5717     QTest::newRow("Any optional and required")
5718         << QString("xxxx") << QString("XXXX") << QString("+-") << QString("+-*/");
5719     QTest::newRow("Numeric (0-9) required")
5720         << QString("0000") << QString("9999") << QString("11") << QString("1138");
5721     QTest::newRow("Numeric (1-9) optional and required")
5722         << QString("dddd") << QString("DDDD") << QString("12") << QString("1234");
5723 }
5724
5725 void tst_qquicktextinput::hasAcceptableInputMask()
5726 {
5727     QFETCH(QString, optionalMask);
5728     QFETCH(QString, requiredMask);
5729     QFETCH(QString, invalid);
5730     QFETCH(QString, valid);
5731
5732     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + optionalMask + "\" }";
5733     QQmlComponent textInputComponent(&engine);
5734     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5735     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5736     QVERIFY(textInput != 0);
5737
5738     QQuickCanvas canvas;
5739     textInput->setParentItem(canvas.rootItem());
5740     canvas.show();
5741     canvas.requestActivateWindow();
5742     QTest::qWaitForWindowShown(&canvas);
5743     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5744
5745     // test that invalid input (for required) work for optionalMask
5746     textInput->setText(invalid);
5747     QVERIFY(textInput->hasAcceptableInput());
5748
5749     // at the moment we don't strip the blank character if it is valid input, this makes the test between x vs X useless
5750     QEXPECT_FAIL( "Any optional and required", "To eat blanks or not? Known issue. Task 43172", Abort);
5751
5752     // test requiredMask
5753     textInput->setInputMask(requiredMask);
5754     textInput->setText(invalid);
5755     QVERIFY(!textInput->hasAcceptableInput());
5756
5757     textInput->setText(valid);
5758     QVERIFY(textInput->hasAcceptableInput());
5759 }
5760
5761 void tst_qquicktextinput::maskCharacter_data()
5762 {
5763     QTest::addColumn<QString>("mask");
5764     QTest::addColumn<QString>("input");
5765     QTest::addColumn<bool>("expectedValid");
5766
5767     QTest::newRow("Hex") << QString("H")
5768                          << QString("0123456789abcdefABCDEF") << true;
5769     QTest::newRow("hex") << QString("h")
5770                          << QString("0123456789abcdefABCDEF") << true;
5771     QTest::newRow("HexInvalid") << QString("H")
5772                                 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5773                                 << false;
5774     QTest::newRow("hexInvalid") << QString("h")
5775                                 << QString("ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ")
5776                                 << false;
5777     QTest::newRow("Bin") << QString("B")
5778                          << QString("01") << true;
5779     QTest::newRow("bin") << QString("b")
5780                          << QString("01") << true;
5781     QTest::newRow("BinInvalid") << QString("B")
5782                                 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5783                                 << false;
5784     QTest::newRow("binInvalid") << QString("b")
5785                                 << QString("23456789qwertyuiopasdfghjklzxcvbnm")
5786                                 << false;
5787 }
5788
5789 void tst_qquicktextinput::maskCharacter()
5790 {
5791     QFETCH(QString, mask);
5792     QFETCH(QString, input);
5793     QFETCH(bool, expectedValid);
5794
5795     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; inputMask: \"" + mask + "\" }";
5796     QQmlComponent textInputComponent(&engine);
5797     textInputComponent.setData(componentStr.toLatin1(), QUrl());
5798     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
5799     QVERIFY(textInput != 0);
5800
5801     QQuickCanvas canvas;
5802     textInput->setParentItem(canvas.rootItem());
5803     canvas.show();
5804     canvas.requestActivateWindow();
5805     QTest::qWaitForWindowShown(&canvas);
5806     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
5807
5808     for (int i = 0; i < input.size(); ++i) {
5809         QString in = QString(input.at(i));
5810         QString expected = expectedValid ? in : QString();
5811         textInput->setText(QString(input.at(i)));
5812         QCOMPARE(textInput->text(), expected);
5813     }
5814 }
5815
5816 QTEST_MAIN(tst_qquicktextinput)
5817
5818 #include "tst_qquicktextinput.moc"