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