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