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