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