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