Remove Q_WS_*, symbian and maemo code in QtDeclarative
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qquicktextinput / tst_qquicktextinput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include "../shared/util.h"
44 #include <QtDeclarative/qdeclarativeengine.h>
45 #include <QFile>
46 #include <QtDeclarative/qquickview.h>
47 #include <QtGui/qguiapplication.h>
48 #include <QInputPanel>
49 #include <private/qquicktextinput_p.h>
50 #include <private/qquicktextinput_p_p.h>
51 #include <QDebug>
52 #include <QDir>
53 #include <QStyle>
54 #include <QInputContext>
55 #include <private/qsgdistancefieldglyphcache_p.h>
56 #include <QtOpenGL/QGLShaderProgram>
57 #include <math.h>
58
59 #include "qplatformdefs.h"
60
61 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
62 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
63
64 #define QTBUG_21691
65 #define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext"
66
67
68 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
69 {
70     // XXX This will be replaced by some clever persistent platform image store.
71     QString persistent_dir = TESTDATA("");
72     QString arch = "unknown-architecture"; // QTest needs to help with this.
73
74     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
75
76     if (!QFile::exists(expectfile)) {
77         actual.save(expectfile);
78         qWarning() << "created" << expectfile;
79     }
80
81     return expectfile;
82 }
83
84 class tst_qquicktextinput : public QObject
85
86 {
87     Q_OBJECT
88 public:
89     tst_qquicktextinput();
90
91 private slots:
92     void initTestCase();
93     void cleanupTestCase();
94     void text();
95     void width();
96     void font();
97     void color();
98     void selection();
99     void isRightToLeft_data();
100     void isRightToLeft();
101     void moveCursorSelection_data();
102     void moveCursorSelection();
103     void moveCursorSelectionSequence_data();
104     void moveCursorSelectionSequence();
105     void dragMouseSelection();
106     void mouseSelectionMode_data();
107     void mouseSelectionMode();
108     void tripleClickSelectsAll();
109
110     void horizontalAlignment_data();
111     void horizontalAlignment();
112     void horizontalAlignment_RightToLeft();
113
114     void positionAt();
115
116     void maxLength();
117     void masks();
118     void validators();
119     void inputMethods();
120
121     void passwordCharacter();
122     void cursorDelegate();
123     void cursorVisible();
124     void cursorRectangle();
125     void navigation();
126     void navigation_RTL();
127     void copyAndPaste();
128     void canPasteEmpty();
129     void canPaste();
130     void readOnly();
131
132     void openInputPanel();
133     void setHAlignClearCache();
134     void focusOutClearSelection();
135
136     void echoMode();
137 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
138     void passwordEchoDelay();
139 #endif
140     void geometrySignals();
141     void testQtQuick11Attributes();
142     void testQtQuick11Attributes_data();
143
144     void preeditAutoScroll();
145     void preeditMicroFocus();
146     void inputContextMouseHandler();
147     void inputMethodComposing();
148     void cursorRectangleSize();
149
150     void QTBUG_19956();
151     void QTBUG_19956_data();
152     void QTBUG_19956_regexp();
153
154 private:
155     void simulateKey(QQuickView *, int key);
156
157     QDeclarativeEngine engine;
158     QStringList standard;
159     QStringList colorStrings;
160 };
161 void tst_qquicktextinput::initTestCase()
162 {
163 }
164
165 void tst_qquicktextinput::cleanupTestCase()
166 {
167
168 }
169 tst_qquicktextinput::tst_qquicktextinput()
170 {
171     standard << "the quick brown fox jumped over the lazy dog"
172         << "It's supercalifragisiticexpialidocious!"
173         << "Hello, world!"
174         << "!dlrow ,olleH"
175         << " spacey   text ";
176
177     colorStrings << "aliceblue"
178                  << "antiquewhite"
179                  << "aqua"
180                  << "darkkhaki"
181                  << "darkolivegreen"
182                  << "dimgray"
183                  << "palevioletred"
184                  << "lightsteelblue"
185                  << "#000000"
186                  << "#AAAAAA"
187                  << "#FFFFFF"
188                  << "#2AC05F";
189 }
190
191 void tst_qquicktextinput::text()
192 {
193     {
194         QDeclarativeComponent textinputComponent(&engine);
195         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\"  }", QUrl());
196         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
197
198         QVERIFY(textinputObject != 0);
199         QCOMPARE(textinputObject->text(), QString(""));
200
201         delete textinputObject;
202     }
203
204     for (int i = 0; i < standard.size(); i++)
205     {
206         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
207         QDeclarativeComponent textinputComponent(&engine);
208         textinputComponent.setData(componentStr.toLatin1(), QUrl());
209         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
210
211         QVERIFY(textinputObject != 0);
212         QCOMPARE(textinputObject->text(), standard.at(i));
213
214         delete textinputObject;
215     }
216
217 }
218
219 void tst_qquicktextinput::width()
220 {
221     // uses Font metrics to find the width for standard
222     {
223         QDeclarativeComponent textinputComponent(&engine);
224         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\" }", QUrl());
225         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
226
227         QVERIFY(textinputObject != 0);
228         QCOMPARE(textinputObject->width(), 0.0);
229
230         delete textinputObject;
231     }
232
233     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
234
235     for (int i = 0; i < standard.size(); i++)
236     {
237         QFont f;
238         qreal metricWidth = 0.0;
239         if (requiresUnhintedMetrics) {
240             QString s = standard.at(i);
241             s.replace(QLatin1Char('\n'), QChar::LineSeparator);
242
243             QTextLayout layout(s);
244             layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
245             {
246                 QTextOption option;
247                 option.setUseDesignMetrics(true);
248                 layout.setTextOption(option);
249             }
250
251             layout.beginLayout();
252             forever {
253                 QTextLine line = layout.createLine();
254                 if (!line.isValid())
255                     break;
256             }
257
258             layout.endLayout();
259
260             metricWidth = ceil(layout.boundingRect().width());
261         } else {
262             QFontMetricsF fm(f);
263             metricWidth = fm.width(standard.at(i));
264         }
265
266         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
267         QDeclarativeComponent textinputComponent(&engine);
268         textinputComponent.setData(componentStr.toLatin1(), QUrl());
269         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
270
271         QVERIFY(textinputObject != 0);
272         int delta = abs(int(int(textinputObject->width()) - metricWidth));
273         QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
274
275         delete textinputObject;
276     }
277 }
278
279 void tst_qquicktextinput::font()
280 {
281     //test size, then bold, then italic, then family
282     {
283         QString componentStr = "import QtQuick 2.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
284         QDeclarativeComponent textinputComponent(&engine);
285         textinputComponent.setData(componentStr.toLatin1(), QUrl());
286         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
287
288         QVERIFY(textinputObject != 0);
289         QCOMPARE(textinputObject->font().pointSize(), 40);
290         QCOMPARE(textinputObject->font().bold(), false);
291         QCOMPARE(textinputObject->font().italic(), false);
292
293         delete textinputObject;
294     }
295
296     {
297         QString componentStr = "import QtQuick 2.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
298         QDeclarativeComponent textinputComponent(&engine);
299         textinputComponent.setData(componentStr.toLatin1(), QUrl());
300         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
301
302         QVERIFY(textinputObject != 0);
303         QCOMPARE(textinputObject->font().bold(), true);
304         QCOMPARE(textinputObject->font().italic(), false);
305
306         delete textinputObject;
307     }
308
309     {
310         QString componentStr = "import QtQuick 2.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
311         QDeclarativeComponent textinputComponent(&engine);
312         textinputComponent.setData(componentStr.toLatin1(), QUrl());
313         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
314
315         QVERIFY(textinputObject != 0);
316         QCOMPARE(textinputObject->font().italic(), true);
317         QCOMPARE(textinputObject->font().bold(), false);
318
319         delete textinputObject;
320     }
321
322     {
323         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
324         QDeclarativeComponent textinputComponent(&engine);
325         textinputComponent.setData(componentStr.toLatin1(), QUrl());
326         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
327
328         QVERIFY(textinputObject != 0);
329         QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
330         QCOMPARE(textinputObject->font().bold(), false);
331         QCOMPARE(textinputObject->font().italic(), false);
332
333         delete textinputObject;
334     }
335
336     {
337         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
338         QDeclarativeComponent textinputComponent(&engine);
339         textinputComponent.setData(componentStr.toLatin1(), QUrl());
340         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
341
342         QVERIFY(textinputObject != 0);
343         QCOMPARE(textinputObject->font().family(), QString(""));
344
345         delete textinputObject;
346     }
347 }
348
349 void tst_qquicktextinput::color()
350 {
351     //test color
352     for (int i = 0; i < colorStrings.size(); i++)
353     {
354         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
355         QDeclarativeComponent textinputComponent(&engine);
356         textinputComponent.setData(componentStr.toLatin1(), QUrl());
357         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
358         QVERIFY(textinputObject != 0);
359         QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
360
361         delete textinputObject;
362     }
363
364     //test selection color
365     for (int i = 0; i < colorStrings.size(); i++)
366     {
367         QString componentStr = "import QtQuick 2.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
368         QDeclarativeComponent textinputComponent(&engine);
369         textinputComponent.setData(componentStr.toLatin1(), QUrl());
370         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
371         QVERIFY(textinputObject != 0);
372         QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
373
374         delete textinputObject;
375     }
376
377     //test selected text color
378     for (int i = 0; i < colorStrings.size(); i++)
379     {
380         QString componentStr = "import QtQuick 2.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
381         QDeclarativeComponent textinputComponent(&engine);
382         textinputComponent.setData(componentStr.toLatin1(), QUrl());
383         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
384         QVERIFY(textinputObject != 0);
385         QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
386
387         delete textinputObject;
388     }
389
390     {
391         QString colorStr = "#AA001234";
392         QColor testColor("#001234");
393         testColor.setAlpha(170);
394
395         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
396         QDeclarativeComponent textinputComponent(&engine);
397         textinputComponent.setData(componentStr.toLatin1(), QUrl());
398         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
399
400         QVERIFY(textinputObject != 0);
401         QCOMPARE(textinputObject->color(), testColor);
402
403         delete textinputObject;
404     }
405 }
406
407 void tst_qquicktextinput::selection()
408 {
409     QString testStr = standard[0];
410     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
411     QDeclarativeComponent textinputComponent(&engine);
412     textinputComponent.setData(componentStr.toLatin1(), QUrl());
413     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
414     QVERIFY(textinputObject != 0);
415
416
417     //Test selection follows cursor
418     for (int i=0; i<= testStr.size(); i++) {
419         textinputObject->setCursorPosition(i);
420         QCOMPARE(textinputObject->cursorPosition(), i);
421         QCOMPARE(textinputObject->selectionStart(), i);
422         QCOMPARE(textinputObject->selectionEnd(), i);
423         QVERIFY(textinputObject->selectedText().isNull());
424     }
425
426     textinputObject->setCursorPosition(0);
427     QVERIFY(textinputObject->cursorPosition() == 0);
428     QVERIFY(textinputObject->selectionStart() == 0);
429     QVERIFY(textinputObject->selectionEnd() == 0);
430     QVERIFY(textinputObject->selectedText().isNull());
431
432     // Verify invalid positions are ignored.
433     textinputObject->setCursorPosition(-1);
434     QVERIFY(textinputObject->cursorPosition() == 0);
435     QVERIFY(textinputObject->selectionStart() == 0);
436     QVERIFY(textinputObject->selectionEnd() == 0);
437     QVERIFY(textinputObject->selectedText().isNull());
438
439     textinputObject->setCursorPosition(textinputObject->text().count()+1);
440     QVERIFY(textinputObject->cursorPosition() == 0);
441     QVERIFY(textinputObject->selectionStart() == 0);
442     QVERIFY(textinputObject->selectionEnd() == 0);
443     QVERIFY(textinputObject->selectedText().isNull());
444
445     //Test selection
446     for (int i=0; i<= testStr.size(); i++) {
447         textinputObject->select(0,i);
448         QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
449     }
450     for (int i=0; i<= testStr.size(); i++) {
451         textinputObject->select(i,testStr.size());
452         QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
453     }
454
455     textinputObject->setCursorPosition(0);
456     QVERIFY(textinputObject->cursorPosition() == 0);
457     QVERIFY(textinputObject->selectionStart() == 0);
458     QVERIFY(textinputObject->selectionEnd() == 0);
459     QVERIFY(textinputObject->selectedText().isNull());
460
461     //Test Error Ignoring behaviour
462     textinputObject->setCursorPosition(0);
463     QVERIFY(textinputObject->selectedText().isNull());
464     textinputObject->select(-10,0);
465     QVERIFY(textinputObject->selectedText().isNull());
466     textinputObject->select(100,110);
467     QVERIFY(textinputObject->selectedText().isNull());
468     textinputObject->select(0,-10);
469     QVERIFY(textinputObject->selectedText().isNull());
470     textinputObject->select(0,100);
471     QVERIFY(textinputObject->selectedText().isNull());
472     textinputObject->select(0,10);
473     QVERIFY(textinputObject->selectedText().size() == 10);
474     textinputObject->select(-10,10);
475     QVERIFY(textinputObject->selectedText().size() == 10);
476     textinputObject->select(100,101);
477     QVERIFY(textinputObject->selectedText().size() == 10);
478     textinputObject->select(0,-10);
479     QVERIFY(textinputObject->selectedText().size() == 10);
480     textinputObject->select(0,100);
481     QVERIFY(textinputObject->selectedText().size() == 10);
482
483     textinputObject->deselect();
484     QVERIFY(textinputObject->selectedText().isNull());
485     textinputObject->select(0,10);
486     QVERIFY(textinputObject->selectedText().size() == 10);
487     textinputObject->deselect();
488     QVERIFY(textinputObject->selectedText().isNull());
489
490     delete textinputObject;
491 }
492
493 void tst_qquicktextinput::isRightToLeft_data()
494 {
495     QTest::addColumn<QString>("text");
496     QTest::addColumn<bool>("emptyString");
497     QTest::addColumn<bool>("firstCharacter");
498     QTest::addColumn<bool>("lastCharacter");
499     QTest::addColumn<bool>("middleCharacter");
500     QTest::addColumn<bool>("startString");
501     QTest::addColumn<bool>("midString");
502     QTest::addColumn<bool>("endString");
503
504     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
505     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
506     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
507     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
508     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
509     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;
510     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
511 }
512
513 void tst_qquicktextinput::isRightToLeft()
514 {
515     QFETCH(QString, text);
516     QFETCH(bool, emptyString);
517     QFETCH(bool, firstCharacter);
518     QFETCH(bool, lastCharacter);
519     QFETCH(bool, middleCharacter);
520     QFETCH(bool, startString);
521     QFETCH(bool, midString);
522     QFETCH(bool, endString);
523
524     QQuickTextInput textInput;
525     textInput.setText(text);
526
527     // first test that the right string is delivered to the QString::isRightToLeft()
528     QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
529     QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
530     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
531     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
532     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
533     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
534     if (text.isEmpty())
535         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
536     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
537
538     // then test that the feature actually works
539     QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
540     QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
541     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
542     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
543     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
544     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
545     if (text.isEmpty())
546         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
547     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
548 }
549
550 void tst_qquicktextinput::moveCursorSelection_data()
551 {
552     QTest::addColumn<QString>("testStr");
553     QTest::addColumn<int>("cursorPosition");
554     QTest::addColumn<int>("movePosition");
555     QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
556     QTest::addColumn<int>("selectionStart");
557     QTest::addColumn<int>("selectionEnd");
558     QTest::addColumn<bool>("reversible");
559
560     // () contains the text selected by the cursor.
561     // <> contains the actual selection.
562
563     QTest::newRow("(t)he|characters")
564             << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
565     QTest::newRow("do(g)|characters")
566             << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
567     QTest::newRow("jum(p)ed|characters")
568             << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
569     QTest::newRow("jumped( )over|characters")
570             << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
571     QTest::newRow("(the )|characters")
572             << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
573     QTest::newRow("( dog)|characters")
574             << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
575     QTest::newRow("( jumped )|characters")
576             << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
577     QTest::newRow("th(e qu)ick|characters")
578             << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
579     QTest::newRow("la(zy d)og|characters")
580             << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
581     QTest::newRow("jum(ped ov)er|characters")
582             << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
583     QTest::newRow("()the|characters")
584             << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
585     QTest::newRow("dog()|characters")
586             << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
587     QTest::newRow("jum()ped|characters")
588             << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
589
590     QTest::newRow("<(t)he>|words")
591             << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
592     QTest::newRow("<do(g)>|words")
593             << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
594     QTest::newRow("<jum(p)ed>|words")
595             << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
596     QTest::newRow("<jumped( )>over|words,ltr")
597             << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
598     QTest::newRow("jumped<( )over>|words,rtl")
599             << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
600     QTest::newRow("<(the )>quick|words,ltr")
601             << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
602     QTest::newRow("<(the )quick>|words,rtl")
603             << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
604     QTest::newRow("<lazy( dog)>|words,ltr")
605             << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
606     QTest::newRow("lazy<( dog)>|words,rtl")
607             << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
608     QTest::newRow("<fox( jumped )>over|words,ltr")
609             << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
610     QTest::newRow("fox<( jumped )over>|words,rtl")
611             << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
612     QTest::newRow("<th(e qu)ick>|words")
613             << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
614     QTest::newRow("<la(zy d)og|words>")
615             << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
616     QTest::newRow("<jum(ped ov)er>|words")
617             << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
618     QTest::newRow("<()>the|words")
619             << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
620     QTest::newRow("dog<()>|words")
621             << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
622     QTest::newRow("jum<()>ped|words")
623             << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
624
625     QTest::newRow("Hello<(,)> |words")
626             << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
627     QTest::newRow("Hello<(, )>world|words,ltr")
628             << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
629     QTest::newRow("Hello<(, )world>|words,rtl")
630             << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
631     QTest::newRow("<Hel(lo, )>world|words,ltr")
632             << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
633     QTest::newRow("<Hel(lo, )world>|words,rtl")
634             << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
635     QTest::newRow("<Hel(lo)>,|words")
636             << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
637     QTest::newRow("Hello<()>,|words")
638             << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
639     QTest::newRow("Hello,<()>|words")
640             << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
641     QTest::newRow("Hello<,( )>world|words,ltr")
642             << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
643     QTest::newRow("Hello,<( )world>|words,rtl")
644             << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
645     QTest::newRow("Hello<,( world)>|words,ltr")
646             << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
647     QTest::newRow("Hello,<( world)>|words,rtl")
648             << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
649     QTest::newRow("Hello<,( world!)>|words,ltr")
650             << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
651     QTest::newRow("Hello,<( world!)>|words,rtl")
652             << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
653     QTest::newRow("Hello<(, world!)>|words")
654             << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
655     // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
656     // QTBUG-11365
657     // QTest::newRow("world<(!)>|words")
658     //         << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
659     QTest::newRow("world!<()>)|words")
660             << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
661     QTest::newRow("world<()>!)|words")
662             << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
663
664     QTest::newRow("<(,)>olleH |words")
665             << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
666     QTest::newRow("<dlrow( ,)>olleH|words,ltr")
667             << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
668     QTest::newRow("dlrow<( ,)>olleH|words,rtl")
669             << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
670     QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
671             << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
672     QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
673             << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
674     QTest::newRow(",<(ol)leH>,|words")
675             << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
676     QTest::newRow(",<()>olleH|words")
677             << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
678     QTest::newRow("<()>,olleH|words")
679             << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
680     QTest::newRow("<dlrow( )>,olleH|words,ltr")
681             << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
682     QTest::newRow("dlrow<( ),>olleH|words,rtl")
683             << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
684     QTest::newRow("<(dlrow )>,olleH|words,ltr")
685             << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
686     QTest::newRow("<(dlrow ),>olleH|words,rtl")
687             << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
688     QTest::newRow("<(!dlrow )>,olleH|words,ltr")
689             << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
690     QTest::newRow("<(!dlrow ),>olleH|words,rtl")
691             << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
692     QTest::newRow("(!dlrow ,)olleH|words")
693             << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
694     QTest::newRow("<(!)>dlrow|words")
695             << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
696     QTest::newRow("<()>!dlrow|words")
697             << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
698     QTest::newRow("!<()>dlrow|words")
699             << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
700
701     QTest::newRow(" <s(pac)ey>   text |words")
702             << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
703     QTest::newRow(" spacey   <t(ex)t> |words")
704             << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
705     QTest::newRow("<( )>spacey   text |words|ltr")
706             << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
707     QTest::newRow("<( )spacey>   text |words|rtl")
708             << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
709     QTest::newRow("spacey   <text( )>|words|ltr")
710             << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
711 //    QTBUG-11365
712 //    QTest::newRow("spacey   text<( )>|words|rtl")
713 //            << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
714     QTest::newRow("<()> spacey   text |words")
715             << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
716     QTest::newRow(" spacey   text <()>|words")
717             << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
718 }
719
720 void tst_qquicktextinput::moveCursorSelection()
721 {
722     QFETCH(QString, testStr);
723     QFETCH(int, cursorPosition);
724     QFETCH(int, movePosition);
725     QFETCH(QQuickTextInput::SelectionMode, mode);
726     QFETCH(int, selectionStart);
727     QFETCH(int, selectionEnd);
728     QFETCH(bool, reversible);
729
730     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
731     QDeclarativeComponent textinputComponent(&engine);
732     textinputComponent.setData(componentStr.toLatin1(), QUrl());
733     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
734     QVERIFY(textinputObject != 0);
735
736     textinputObject->setCursorPosition(cursorPosition);
737     textinputObject->moveCursorSelection(movePosition, mode);
738
739     QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
740     QCOMPARE(textinputObject->selectionStart(), selectionStart);
741     QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
742
743     if (reversible) {
744         textinputObject->setCursorPosition(movePosition);
745         textinputObject->moveCursorSelection(cursorPosition, mode);
746
747         QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
748         QCOMPARE(textinputObject->selectionStart(), selectionStart);
749         QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
750     }
751
752     delete textinputObject;
753 }
754
755 void tst_qquicktextinput::moveCursorSelectionSequence_data()
756 {
757     QTest::addColumn<QString>("testStr");
758     QTest::addColumn<int>("cursorPosition");
759     QTest::addColumn<int>("movePosition1");
760     QTest::addColumn<int>("movePosition2");
761     QTest::addColumn<int>("selection1Start");
762     QTest::addColumn<int>("selection1End");
763     QTest::addColumn<int>("selection2Start");
764     QTest::addColumn<int>("selection2End");
765
766     // () contains the text selected by the cursor.
767     // <> contains the actual selection.
768     // ^ is the revised cursor position.
769     // {} contains the revised selection.
770
771     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
772             << standard[0]
773             << 9 << 13 << 17
774             << 4 << 15
775             << 4 << 19;
776     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
777             << standard[0]
778             << 13 << 9 << 17
779             << 9 << 15
780             << 10 << 19;
781     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
782             << standard[0]
783             << 9 << 13 << 16
784             << 4 << 15
785             << 4 << 16;
786     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
787             << standard[0]
788             << 13 << 9 << 16
789             << 9 << 15
790             << 10 << 16;
791     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
792             << standard[0]
793             << 9 << 13 << 15
794             << 4 << 15
795             << 4 << 15;
796     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
797             << standard[0]
798             << 13 << 9 << 15
799             << 9 << 15
800             << 10 << 15;
801     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
802             << standard[0]
803             << 9 << 13 << 10
804             << 4 << 15
805             << 4 << 10;
806     QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
807             << standard[0]
808             << 13 << 9 << 10
809             << 9 << 15
810             << 10 << 15;
811     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
812             << standard[0]
813             << 9 << 13 << 9
814             << 4 << 15
815             << 4 << 9;
816     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
817             << standard[0]
818             << 13 << 9 << 9
819             << 9 << 15
820             << 9 << 15;
821     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
822             << standard[0]
823             << 9 << 13 << 7
824             << 4 << 15
825             << 4 << 9;
826     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
827             << standard[0]
828             << 13 << 9 << 7
829             << 9 << 15
830             << 4 << 15;
831     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
832             << standard[0]
833             << 9 << 13 << 4
834             << 4 << 15
835             << 4 << 9;
836     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
837             << standard[0]
838             << 13 << 9 << 4
839             << 9 << 15
840             << 4 << 15;
841     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
842             << standard[0]
843             << 9 << 13 << 3
844             << 4 << 15
845             << 3 << 9;
846     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
847             << standard[0]
848             << 13 << 9 << 3
849             << 9 << 15
850             << 3 << 15;
851     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
852             << standard[0]
853             << 9 << 13 << 1
854             << 4 << 15
855             << 0 << 9;
856     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
857             << standard[0]
858             << 13 << 9 << 1
859             << 9 << 15
860             << 0 << 15;
861
862     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
863             << standard[2]
864             << 2 << 4 << 8
865             << 0 << 5
866             << 0 << 12;
867     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
868             << standard[2]
869             << 4 << 2 << 8
870             << 0 << 5
871             << 0 << 12;
872
873     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
874             << standard[3]
875             << 9 << 11 << 5
876             << 8 << 13
877             << 1 << 13;
878     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
879             << standard[3]
880             << 11 << 9 << 5
881             << 8 << 13
882             << 1 << 13;
883
884     QTest::newRow("{<(^} sp)acey>   text |ltr")
885             << standard[4]
886             << 0 << 3 << 0
887             << 0 << 7
888             << 0 << 0;
889     QTest::newRow("{<( ^}sp)acey>   text |ltr")
890             << standard[4]
891             << 0 << 3 << 1
892             << 0 << 7
893             << 0 << 1;
894     QTest::newRow("<( {s^p)acey>}   text |rtl")
895             << standard[4]
896             << 3 << 0 << 2
897             << 0 << 7
898             << 1 << 7;
899     QTest::newRow("<( {^sp)acey>}   text |rtl")
900             << standard[4]
901             << 3 << 0 << 1
902             << 0 << 7
903             << 1 << 7;
904
905     QTest::newRow(" spacey   <te(xt {^)>}|rtl")
906             << standard[4]
907             << 15 << 12 << 15
908             << 10 << 15
909             << 15 << 15;
910 //    QTBUG-11365
911 //    QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
912 //            << standard[4]
913 //            << 15 << 12 << 14
914 //            << 10 << 15
915 //            << 14 << 15;
916     QTest::newRow(" spacey   {<te(x^t} )>|ltr")
917             << standard[4]
918             << 12 << 15 << 13
919             << 10 << 15
920             << 10 << 14;
921 //    QTBUG-11365
922 //    QTest::newRow(" spacey   {<te(xt^} )>|ltr")
923 //            << standard[4]
924 //            << 12 << 15 << 14
925 //            << 10 << 15
926 //            << 10 << 14;
927 }
928
929 void tst_qquicktextinput::moveCursorSelectionSequence()
930 {
931     QFETCH(QString, testStr);
932     QFETCH(int, cursorPosition);
933     QFETCH(int, movePosition1);
934     QFETCH(int, movePosition2);
935     QFETCH(int, selection1Start);
936     QFETCH(int, selection1End);
937     QFETCH(int, selection2Start);
938     QFETCH(int, selection2End);
939
940     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
941     QDeclarativeComponent textinputComponent(&engine);
942     textinputComponent.setData(componentStr.toLatin1(), QUrl());
943     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
944     QVERIFY(textinputObject != 0);
945
946     textinputObject->setCursorPosition(cursorPosition);
947
948     textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
949     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
950     QCOMPARE(textinputObject->selectionStart(), selection1Start);
951     QCOMPARE(textinputObject->selectionEnd(), selection1End);
952
953     textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
954     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
955     QCOMPARE(textinputObject->selectionStart(), selection2Start);
956     QCOMPARE(textinputObject->selectionEnd(), selection2End);
957
958     delete textinputObject;
959 }
960
961 void tst_qquicktextinput::dragMouseSelection()
962 {
963     QString qmlfile = TESTDATA("mouseselection_true.qml");
964
965     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
966
967     canvas.show();
968     canvas.requestActivateWindow();
969     QTest::qWaitForWindowShown(&canvas);
970
971     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
972
973     QVERIFY(canvas.rootObject() != 0);
974     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
975     QVERIFY(textInputObject != 0);
976
977     // press-and-drag-and-release from x1 to x2
978     int x1 = 10;
979     int x2 = 70;
980     int y = textInputObject->height()/2;
981     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
982     QTest::mouseMove(&canvas, QPoint(x2, y));
983     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
984     QTest::qWait(100);
985     QString str1;
986     QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
987     QVERIFY(str1.length() > 3);
988
989     // press and drag the current selection.
990     x1 = 40;
991     x2 = 100;
992     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
993     QTest::mouseMove(&canvas, QPoint(x2, y));
994     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
995     QTest::qWait(300);
996     QString str2 = textInputObject->selectedText();
997     QVERIFY(str2.length() > 3);
998
999     QVERIFY(str1 != str2);
1000 }
1001
1002 void tst_qquicktextinput::mouseSelectionMode_data()
1003 {
1004     QTest::addColumn<QString>("qmlfile");
1005     QTest::addColumn<bool>("selectWords");
1006
1007     // import installed
1008     QTest::newRow("SelectWords") << TESTDATA("mouseselectionmode_words.qml") << true;
1009     QTest::newRow("SelectCharacters") << TESTDATA("mouseselectionmode_characters.qml") << false;
1010     QTest::newRow("default") << TESTDATA("mouseselectionmode_default.qml") << false;
1011 }
1012
1013 void tst_qquicktextinput::mouseSelectionMode()
1014 {
1015     QFETCH(QString, qmlfile);
1016     QFETCH(bool, selectWords);
1017
1018     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1019
1020     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1021
1022     canvas.show();
1023     canvas.requestActivateWindow();
1024     QTest::qWaitForWindowShown(&canvas);
1025     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1026
1027     QVERIFY(canvas.rootObject() != 0);
1028     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1029     QVERIFY(textInputObject != 0);
1030
1031     // press-and-drag-and-release from x1 to x2
1032     int x1 = 10;
1033     int x2 = 70;
1034     int y = textInputObject->height()/2;
1035     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1036     QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1037     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1038     QTest::qWait(300);
1039     if (selectWords) {
1040         QTRY_COMPARE(textInputObject->selectedText(), text);
1041     } else {
1042         QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1043         QVERIFY(textInputObject->selectedText() != text);
1044     }
1045 }
1046
1047 void tst_qquicktextinput::horizontalAlignment_data()
1048 {
1049     QTest::addColumn<int>("hAlign");
1050     QTest::addColumn<QString>("expectfile");
1051
1052     QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1053     QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1054     QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1055 }
1056
1057 void tst_qquicktextinput::horizontalAlignment()
1058 {
1059     QSKIP("Image comparison of text is almost guaranteed to fail during development");
1060
1061     QFETCH(int, hAlign);
1062     QFETCH(QString, expectfile);
1063
1064     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment.qml")));
1065
1066     canvas.show();
1067     canvas.requestActivateWindow();
1068     QTest::qWaitForWindowShown(&canvas);
1069     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1070     QObject *ob = canvas.rootObject();
1071     QVERIFY(ob != 0);
1072     ob->setProperty("horizontalAlignment",hAlign);
1073     QImage actual = canvas.grabFrameBuffer();
1074
1075     expectfile = createExpectedFileIfNotFound(expectfile, actual);
1076
1077     QImage expect(expectfile);
1078
1079     QCOMPARE(actual,expect);
1080 }
1081
1082 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1083 {
1084     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment_RightToLeft.qml")));
1085     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1086     QVERIFY(textInput != 0);
1087     canvas.show();
1088
1089     const QString rtlText = textInput->text();
1090
1091     QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1092     QVERIFY(textInputPrivate != 0);
1093     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1094
1095     // implicit alignment should follow the reading direction of RTL text
1096     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1097     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1098     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1099
1100     // explicitly left aligned
1101     textInput->setHAlign(QQuickTextInput::AlignLeft);
1102     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1103     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1104     QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1105
1106     // explicitly right aligned
1107     textInput->setHAlign(QQuickTextInput::AlignRight);
1108     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1109     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1110     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1111
1112     // explicitly center aligned
1113     textInput->setHAlign(QQuickTextInput::AlignHCenter);
1114     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1115     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1116     QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1117     QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width > canvas.width()/2);
1118
1119     // reseted alignment should go back to following the text reading direction
1120     textInput->resetHAlign();
1121     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1122     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1123     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1124
1125     // mirror the text item
1126     QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1127
1128     // mirrored implicit alignment should continue to follow the reading direction of the text
1129     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1130     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1131     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1132
1133     // explicitly right aligned behaves as left aligned
1134     textInput->setHAlign(QQuickTextInput::AlignRight);
1135     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1136     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1137     QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1138
1139     // mirrored explicitly left aligned behaves as right aligned
1140     textInput->setHAlign(QQuickTextInput::AlignLeft);
1141     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1142     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1143     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1144
1145     // disable mirroring
1146     QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1147     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1148     textInput->resetHAlign();
1149
1150     // English text should be implicitly left aligned
1151     textInput->setText("Hello world!");
1152     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1153     QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1154
1155     canvas.requestActivateWindow();
1156     QTest::qWaitForWindowShown(&canvas);
1157     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1158
1159     // If there is no commited text, the preedit text should determine the alignment.
1160     textInput->setText(QString());
1161     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
1162     QEXPECT_FAIL("", "QTBUG-21691", Continue);
1163     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1164     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
1165     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1166
1167 #ifdef Q_OS_MAC
1168     // empty text with implicit alignment follows the system locale-based
1169     // keyboard input direction from QGuiApplication::keyboardInputDirection
1170     QEXPECT_FAIL("", "QTBUG-18040", Abort);
1171 #endif
1172     textInput->setText("");
1173     QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1174                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1175     if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1176         QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1177     else
1178         QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1179     textInput->setHAlign(QQuickTextInput::AlignRight);
1180     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1181     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1182
1183
1184 #ifdef Q_OS_MAC
1185     QEXPECT_FAIL("", "QTBUG-18040", Abort); // alignment of TextInput with no text set to it
1186 #endif
1187     QString componentStr = "import QtQuick 2.0\nTextInput {}";
1188     QDeclarativeComponent textComponent(&engine);
1189     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1190     QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1191     QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1192                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1193     delete textObject;
1194 }
1195
1196 void tst_qquicktextinput::positionAt()
1197 {
1198     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1199     QVERIFY(canvas.rootObject() != 0);
1200     canvas.show();
1201     canvas.requestActivateWindow();
1202     QTest::qWaitForWindowShown(&canvas);
1203
1204     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1205     QVERIFY(textinputObject != 0);
1206
1207     // Check autoscrolled...
1208     QFontMetrics fm(textinputObject->font());
1209
1210     int pos = textinputObject->positionAt(textinputObject->width()/2);
1211     int textWidth = 0;
1212     int textLeftWidth = 0;
1213     if (!qmlDisableDistanceField()) {
1214         {
1215             QTextLayout layout(textinputObject->text().left(pos));
1216
1217             {
1218                 QTextOption option;
1219                 option.setUseDesignMetrics(true);
1220                 layout.setTextOption(option);
1221             }
1222
1223             layout.beginLayout();
1224             QTextLine line = layout.createLine();
1225             layout.endLayout();
1226
1227             textLeftWidth = ceil(line.horizontalAdvance());
1228         }
1229         {
1230             QTextLayout layout(textinputObject->text());
1231
1232             {
1233                 QTextOption option;
1234                 option.setUseDesignMetrics(true);
1235                 layout.setTextOption(option);
1236             }
1237
1238             layout.beginLayout();
1239             QTextLine line = layout.createLine();
1240             layout.endLayout();
1241
1242             textWidth = ceil(line.horizontalAdvance());
1243         }
1244     } else {
1245         textWidth = fm.width(textinputObject->text());
1246         textLeftWidth = fm.width(textinputObject->text().left(pos));
1247     }
1248
1249     int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1250
1251     // some tollerance for different fonts.
1252     QEXPECT_FAIL("", "QTBUG-21689", Abort);
1253 #ifdef Q_OS_LINUX
1254     QVERIFY(diff < 2);
1255 #else
1256     QVERIFY(diff < 5);
1257 #endif
1258
1259     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1260     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1261     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1262
1263     // Check without autoscroll...
1264     textinputObject->setAutoScroll(false);
1265     pos = textinputObject->positionAt(textinputObject->width()/2);
1266
1267     if (!qmlDisableDistanceField()) {
1268         {
1269             QTextLayout layout(textinputObject->text().left(pos));
1270
1271             {
1272                 QTextOption option;
1273                 option.setUseDesignMetrics(true);
1274                 layout.setTextOption(option);
1275             }
1276
1277             layout.beginLayout();
1278             QTextLine line = layout.createLine();
1279             layout.endLayout();
1280
1281             textLeftWidth = ceil(line.horizontalAdvance());
1282         }
1283     } else {
1284         textLeftWidth = fm.width(textinputObject->text().left(pos));
1285     }
1286
1287     diff = abs(int(textLeftWidth-textinputObject->width()/2));
1288
1289     // some tollerance for different fonts.
1290 #ifdef Q_OS_LINUX
1291     QVERIFY(diff < 2);
1292 #else
1293     QVERIFY(diff < 5);
1294 #endif
1295
1296     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1297     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1298     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1299
1300     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1301     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1302
1303     QString preeditText = textinputObject->text().mid(0, pos);
1304     textinputObject->setText(textinputObject->text().mid(pos));
1305     textinputObject->setCursorPosition(0);
1306
1307     QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1308     QGuiApplication::sendEvent(&canvas, &inputEvent);
1309
1310     // Check all points within the preedit text return the same position.
1311     QCOMPARE(textinputObject->positionAt(0), 0);
1312     QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1313     QCOMPARE(textinputObject->positionAt(x0), 0);
1314
1315     // Verify positioning returns to normal after the preedit text.
1316     QCOMPARE(textinputObject->positionAt(x1), 1);
1317     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1318 }
1319
1320 void tst_qquicktextinput::maxLength()
1321 {
1322     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("maxLength.qml")));
1323     QVERIFY(canvas.rootObject() != 0);
1324     canvas.show();
1325     canvas.requestActivateWindow();
1326     QTest::qWaitForWindowShown(&canvas);
1327
1328     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1329     QVERIFY(textinputObject != 0);
1330     QVERIFY(textinputObject->text().isEmpty());
1331     QVERIFY(textinputObject->maxLength() == 10);
1332     foreach (const QString &str, standard) {
1333         QVERIFY(textinputObject->text().length() <= 10);
1334         textinputObject->setText(str);
1335         QVERIFY(textinputObject->text().length() <= 10);
1336     }
1337
1338     textinputObject->setText("");
1339     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1340     for (int i=0; i<20; i++) {
1341         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1342         //simulateKey(&canvas, Qt::Key_A);
1343         QTest::keyPress(&canvas, Qt::Key_A);
1344         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1345         QTest::qWait(50);
1346     }
1347 }
1348
1349 void tst_qquicktextinput::masks()
1350 {
1351     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1352     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1353     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("masks.qml")));
1354     canvas.show();
1355     canvas.requestActivateWindow();
1356     QVERIFY(canvas.rootObject() != 0);
1357     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1358     QVERIFY(textinputObject != 0);
1359     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1360     QVERIFY(textinputObject->text().length() == 0);
1361     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1362     for (int i=0; i<10; i++) {
1363         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1364         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1365         //simulateKey(&canvas, Qt::Key_A);
1366         QTest::keyPress(&canvas, Qt::Key_A);
1367         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1368         QTest::qWait(50);
1369     }
1370 }
1371
1372 void tst_qquicktextinput::validators()
1373 {
1374     // Note that this test assumes that the validators are working properly
1375     // so you may need to run their tests first. All validators are checked
1376     // here to ensure that their exposure to QML is working.
1377
1378     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("validators.qml")));
1379     canvas.show();
1380     canvas.requestActivateWindow();
1381
1382     QVERIFY(canvas.rootObject() != 0);
1383
1384     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1385     QVERIFY(intInput);
1386     intInput->setFocus(true);
1387     QTRY_VERIFY(intInput->hasActiveFocus());
1388     QTest::keyPress(&canvas, Qt::Key_1);
1389     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1390     QTest::qWait(50);
1391     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1392     QCOMPARE(intInput->hasAcceptableInput(), false);
1393     QTest::keyPress(&canvas, Qt::Key_2);
1394     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1395     QTest::qWait(50);
1396     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1397     QCOMPARE(intInput->hasAcceptableInput(), false);
1398     QTest::keyPress(&canvas, Qt::Key_1);
1399     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1400     QTest::qWait(50);
1401     QCOMPARE(intInput->text(), QLatin1String("11"));
1402     QCOMPARE(intInput->hasAcceptableInput(), true);
1403     QTest::keyPress(&canvas, Qt::Key_0);
1404     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1405     QTest::qWait(50);
1406     QCOMPARE(intInput->text(), QLatin1String("11"));
1407     QCOMPARE(intInput->hasAcceptableInput(), true);
1408
1409     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1410     QTRY_VERIFY(dblInput);
1411     dblInput->setFocus(true);
1412     QVERIFY(dblInput->hasActiveFocus() == true);
1413     QTest::keyPress(&canvas, Qt::Key_1);
1414     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1415     QTest::qWait(50);
1416     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1417     QCOMPARE(dblInput->hasAcceptableInput(), false);
1418     QTest::keyPress(&canvas, Qt::Key_2);
1419     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1420     QTest::qWait(50);
1421     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1422     QCOMPARE(dblInput->hasAcceptableInput(), true);
1423     QTest::keyPress(&canvas, Qt::Key_Period);
1424     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1425     QTest::qWait(50);
1426     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1427     QCOMPARE(dblInput->hasAcceptableInput(), true);
1428     QTest::keyPress(&canvas, Qt::Key_1);
1429     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1430     QTest::qWait(50);
1431     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1432     QCOMPARE(dblInput->hasAcceptableInput(), true);
1433     QTest::keyPress(&canvas, Qt::Key_1);
1434     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1435     QTest::qWait(50);
1436     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1437     QCOMPARE(dblInput->hasAcceptableInput(), true);
1438     QTest::keyPress(&canvas, Qt::Key_1);
1439     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1440     QTest::qWait(50);
1441     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1442     QCOMPARE(dblInput->hasAcceptableInput(), true);
1443
1444     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1445     QTRY_VERIFY(strInput);
1446     strInput->setFocus(true);
1447     QVERIFY(strInput->hasActiveFocus() == true);
1448     QTest::keyPress(&canvas, Qt::Key_1);
1449     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1450     QTest::qWait(50);
1451     QTRY_COMPARE(strInput->text(), QLatin1String(""));
1452     QCOMPARE(strInput->hasAcceptableInput(), false);
1453     QTest::keyPress(&canvas, Qt::Key_A);
1454     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1455     QTest::qWait(50);
1456     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1457     QCOMPARE(strInput->hasAcceptableInput(), false);
1458     QTest::keyPress(&canvas, Qt::Key_A);
1459     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1460     QTest::qWait(50);
1461     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1462     QCOMPARE(strInput->hasAcceptableInput(), true);
1463     QTest::keyPress(&canvas, Qt::Key_A);
1464     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1465     QTest::qWait(50);
1466     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1467     QCOMPARE(strInput->hasAcceptableInput(), true);
1468     QTest::keyPress(&canvas, Qt::Key_A);
1469     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1470     QTest::qWait(50);
1471     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1472     QCOMPARE(strInput->hasAcceptableInput(), true);
1473     QTest::keyPress(&canvas, Qt::Key_A);
1474     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1475     QTest::qWait(50);
1476     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1477     QCOMPARE(strInput->hasAcceptableInput(), true);
1478 }
1479
1480 void tst_qquicktextinput::inputMethods()
1481 {
1482     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("inputmethods.qml")));
1483     canvas.show();
1484     canvas.requestActivateWindow();
1485     QTest::qWaitForWindowShown(&canvas);
1486
1487     // test input method hints
1488     QVERIFY(canvas.rootObject() != 0);
1489     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1490     QVERIFY(input != 0);
1491     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1492     input->setInputMethodHints(Qt::ImhUppercaseOnly);
1493     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1494
1495     input->setFocus(true);
1496     QVERIFY(input->hasActiveFocus() == true);
1497     // test that input method event is committed
1498     QInputMethodEvent event;
1499     event.setCommitString( "My ", -12, 0);
1500     QGuiApplication::sendEvent(&canvas, &event);
1501     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
1502     QCOMPARE(input->text(), QString("My Hello world!"));
1503
1504     input->setCursorPosition(2);
1505     event.setCommitString("Your", -2, 2);
1506     QGuiApplication::sendEvent(&canvas, &event);
1507     QCOMPARE(input->text(), QString("Your Hello world!"));
1508     QCOMPARE(input->cursorPosition(), 4);
1509
1510     input->setCursorPosition(7);
1511     event.setCommitString("Goodbye", -2, 5);
1512     QGuiApplication::sendEvent(&canvas, &event);
1513     QCOMPARE(input->text(), QString("Your Goodbye world!"));
1514     QCOMPARE(input->cursorPosition(), 12);
1515
1516     input->setCursorPosition(8);
1517     event.setCommitString("Our", -8, 4);
1518     QGuiApplication::sendEvent(&canvas, &event);
1519     QCOMPARE(input->text(), QString("Our Goodbye world!"));
1520     QCOMPARE(input->cursorPosition(), 7);
1521 }
1522
1523 /*
1524 TextInput element should only handle left/right keys until the cursor reaches
1525 the extent of the text, then they should ignore the keys.
1526
1527 */
1528 void tst_qquicktextinput::navigation()
1529 {
1530     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1531     canvas.show();
1532     canvas.requestActivateWindow();
1533
1534     QVERIFY(canvas.rootObject() != 0);
1535
1536     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1537
1538     QVERIFY(input != 0);
1539     input->setCursorPosition(0);
1540     QTRY_VERIFY(input->hasActiveFocus() == true);
1541     simulateKey(&canvas, Qt::Key_Left);
1542     QVERIFY(input->hasActiveFocus() == false);
1543     simulateKey(&canvas, Qt::Key_Right);
1544     QVERIFY(input->hasActiveFocus() == true);
1545     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1546     input->setCursorPosition(input->text().length());
1547     input->select(0,input->text().length());
1548     QVERIFY(input->selectionStart() != input->selectionEnd());
1549     simulateKey(&canvas, Qt::Key_Right);
1550     QVERIFY(input->selectionStart() == input->selectionEnd());
1551     QVERIFY(input->selectionStart() == input->text().length());
1552     QVERIFY(input->hasActiveFocus() == true);
1553     simulateKey(&canvas, Qt::Key_Right);
1554     QVERIFY(input->hasActiveFocus() == false);
1555     simulateKey(&canvas, Qt::Key_Left);
1556     QVERIFY(input->hasActiveFocus() == true);
1557
1558     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1559     input->setCursorPosition(2);
1560     QCOMPARE(input->cursorPosition(),2);
1561     simulateKey(&canvas, Qt::Key_Up);
1562     QCOMPARE(input->cursorPosition(),2);
1563     simulateKey(&canvas, Qt::Key_Down);
1564     QCOMPARE(input->cursorPosition(),2);
1565 }
1566
1567 void tst_qquicktextinput::navigation_RTL()
1568 {
1569     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1570     canvas.show();
1571     canvas.requestActivateWindow();
1572
1573     QVERIFY(canvas.rootObject() != 0);
1574
1575     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1576
1577     QVERIFY(input != 0);
1578     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1579     input->setText(QString::fromUtf16(arabic_str, 11));
1580
1581     input->setCursorPosition(0);
1582     QTRY_VERIFY(input->hasActiveFocus() == true);
1583
1584     // move off
1585     simulateKey(&canvas, Qt::Key_Right);
1586     QVERIFY(input->hasActiveFocus() == false);
1587
1588     // move back
1589     simulateKey(&canvas, Qt::Key_Left);
1590     QVERIFY(input->hasActiveFocus() == true);
1591
1592     input->setCursorPosition(input->text().length());
1593     QVERIFY(input->hasActiveFocus() == true);
1594
1595     // move off
1596     simulateKey(&canvas, Qt::Key_Left);
1597     QVERIFY(input->hasActiveFocus() == false);
1598
1599     // move back
1600     simulateKey(&canvas, Qt::Key_Right);
1601     QVERIFY(input->hasActiveFocus() == true);
1602 }
1603
1604 void tst_qquicktextinput::copyAndPaste() {
1605 #ifndef QT_NO_CLIPBOARD
1606
1607 #ifdef Q_OS_MAC
1608     {
1609         PasteboardRef pasteboard;
1610         OSStatus status = PasteboardCreate(0, &pasteboard);
1611         if (status == noErr)
1612             CFRelease(pasteboard);
1613         else
1614             QSKIP("This machine doesn't support the clipboard");
1615     }
1616 #endif
1617
1618     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1619     QDeclarativeComponent textInputComponent(&engine);
1620     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1621     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1622     QVERIFY(textInput != 0);
1623
1624     // copy and paste
1625     QCOMPARE(textInput->text().length(), 12);
1626     textInput->select(0, textInput->text().length());;
1627     textInput->copy();
1628     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1629     QCOMPARE(textInput->selectedText().length(), 12);
1630     textInput->setCursorPosition(0);
1631     QVERIFY(textInput->canPaste());
1632     textInput->paste();
1633     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1634     QCOMPARE(textInput->text().length(), 24);
1635
1636     // can paste
1637     QVERIFY(textInput->canPaste());
1638     textInput->setReadOnly(true);
1639     QVERIFY(!textInput->canPaste());
1640     textInput->setReadOnly(false);
1641     QVERIFY(textInput->canPaste());
1642
1643     // select word
1644     textInput->setCursorPosition(0);
1645     textInput->selectWord();
1646     QCOMPARE(textInput->selectedText(), QString("Hello"));
1647
1648     // select all and cut
1649     textInput->selectAll();
1650     textInput->cut();
1651     QCOMPARE(textInput->text().length(), 0);
1652     textInput->paste();
1653     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1654     QCOMPARE(textInput->text().length(), 24);
1655
1656     // clear copy buffer
1657     QClipboard *clipboard = QGuiApplication::clipboard();
1658     QVERIFY(clipboard);
1659     clipboard->clear();
1660     QVERIFY(!textInput->canPaste());
1661
1662     // test that copy functionality is disabled
1663     // when echo mode is set to hide text/password mode
1664     int index = 0;
1665     while (index < 4) {
1666         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1667         textInput->setEchoMode(echoMode);
1668         textInput->setText("My password");
1669         textInput->select(0, textInput->text().length());;
1670         textInput->copy();
1671         if (echoMode == QQuickTextInput::Normal) {
1672             QVERIFY(!clipboard->text().isEmpty());
1673             QCOMPARE(clipboard->text(), QString("My password"));
1674             clipboard->clear();
1675         } else {
1676             QVERIFY(clipboard->text().isEmpty());
1677         }
1678         index++;
1679     }
1680
1681     delete textInput;
1682 #endif
1683 }
1684
1685 void tst_qquicktextinput::canPasteEmpty() {
1686 #ifndef QT_NO_CLIPBOARD
1687
1688     QGuiApplication::clipboard()->clear();
1689
1690     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1691     QDeclarativeComponent textInputComponent(&engine);
1692     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1693     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1694     QVERIFY(textInput != 0);
1695
1696     QLineControl lc;
1697     bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1698     QCOMPARE(textInput->canPaste(), cp);
1699
1700 #endif
1701 }
1702
1703 void tst_qquicktextinput::canPaste() {
1704 #ifndef QT_NO_CLIPBOARD
1705
1706     QGuiApplication::clipboard()->setText("Some text");
1707
1708     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1709     QDeclarativeComponent textInputComponent(&engine);
1710     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1711     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1712     QVERIFY(textInput != 0);
1713
1714     QLineControl lc;
1715     bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1716     QCOMPARE(textInput->canPaste(), cp);
1717
1718 #endif
1719 }
1720
1721 void tst_qquicktextinput::passwordCharacter()
1722 {
1723     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1724     QDeclarativeComponent textInputComponent(&engine);
1725     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1726     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1727     QVERIFY(textInput != 0);
1728
1729     textInput->setPasswordCharacter("X");
1730     qreal implicitWidth = textInput->implicitWidth();
1731     textInput->setPasswordCharacter(".");
1732
1733     // QTBUG-12383 content is updated and redrawn
1734     QVERIFY(textInput->implicitWidth() < implicitWidth);
1735
1736     delete textInput;
1737 }
1738
1739 void tst_qquicktextinput::cursorDelegate()
1740 {
1741     QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1742     view.show();
1743     view.requestActivateWindow();
1744     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1745     QVERIFY(textInputObject != 0);
1746     QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1747     //Test Delegate gets created
1748     textInputObject->setFocus(true);
1749     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1750     QVERIFY(delegateObject);
1751     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1752     //Test Delegate gets moved
1753     for (int i=0; i<= textInputObject->text().length(); i++) {
1754         textInputObject->setCursorPosition(i);
1755         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1756         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1757     }
1758     textInputObject->setCursorPosition(0);
1759     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1760     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1761     //Test Delegate gets deleted
1762     textInputObject->setCursorDelegate(0);
1763     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1764 }
1765
1766 void tst_qquicktextinput::cursorVisible()
1767 {
1768     QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1769     view.show();
1770     view.requestActivateWindow();
1771     QTest::qWaitForWindowShown(&view);
1772     QTRY_COMPARE(&view, qGuiApp->focusWindow());
1773
1774     QQuickTextInput input;
1775     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1776
1777     QCOMPARE(input.isCursorVisible(), false);
1778
1779     input.setCursorVisible(true);
1780     QCOMPARE(input.isCursorVisible(), true);
1781     QCOMPARE(spy.count(), 1);
1782
1783     input.setCursorVisible(false);
1784     QCOMPARE(input.isCursorVisible(), false);
1785     QCOMPARE(spy.count(), 2);
1786
1787     input.setFocus(true);
1788     QCOMPARE(input.isCursorVisible(), false);
1789     QCOMPARE(spy.count(), 2);
1790
1791     input.setParentItem(view.rootObject());
1792     QCOMPARE(input.isCursorVisible(), true);
1793     QCOMPARE(spy.count(), 3);
1794
1795     input.setFocus(false);
1796     QCOMPARE(input.isCursorVisible(), false);
1797     QCOMPARE(spy.count(), 4);
1798
1799     input.setFocus(true);
1800     QCOMPARE(input.isCursorVisible(), true);
1801     QCOMPARE(spy.count(), 5);
1802
1803     QQuickView alternateView;
1804     alternateView.show();
1805     alternateView.requestActivateWindow();
1806     QTest::qWaitForWindowShown(&alternateView);
1807
1808     QCOMPARE(input.isCursorVisible(), false);
1809     QCOMPARE(spy.count(), 6);
1810
1811     view.requestActivateWindow();
1812     QTest::qWaitForWindowShown(&view);
1813     QCOMPARE(input.isCursorVisible(), true);
1814     QCOMPARE(spy.count(), 7);
1815 }
1816
1817 void tst_qquicktextinput::cursorRectangle()
1818 {
1819     QSKIP("QTBUG-21689");
1820
1821     QString text = "Hello World!";
1822
1823     QQuickTextInput input;
1824     input.setText(text);
1825     QFontMetricsF fm(input.font());
1826     input.setWidth(fm.width(text.mid(0, 5)));
1827
1828     QRect r;
1829
1830     // some tolerance for different fonts.
1831 #ifdef Q_OS_LINUX
1832     const int error = 2;
1833 #else
1834     const int error = 5;
1835 #endif
1836
1837
1838     for (int i = 0; i <= 5; ++i) {
1839         input.setCursorPosition(i);
1840         r = input.cursorRectangle();
1841         int textWidth = fm.width(text.mid(0, i));
1842
1843         QVERIFY(r.left() < textWidth + error);
1844         QVERIFY(r.right() > textWidth - error);
1845         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1846     }
1847
1848     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1849     QVERIFY(r.left() < input.boundingRect().width());
1850     QVERIFY(r.right() >= input.width() - error);
1851
1852     for (int i = 6; i < text.length(); ++i) {
1853         input.setCursorPosition(i);
1854         QCOMPARE(r, input.cursorRectangle());
1855         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1856     }
1857
1858     for (int i = text.length() - 2; i >= 0; --i) {
1859         input.setCursorPosition(i);
1860         r = input.cursorRectangle();
1861         QVERIFY(r.right() >= 0);
1862         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1863     }
1864
1865     input.setText("Hi!");
1866     input.setHAlign(QQuickTextInput::AlignRight);
1867     r = input.cursorRectangle();
1868     QVERIFY(r.left() < input.boundingRect().width());
1869     QVERIFY(r.right() >= input.width() - error);
1870 }
1871
1872 void tst_qquicktextinput::readOnly()
1873 {
1874     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1875     canvas.show();
1876     canvas.requestActivateWindow();
1877
1878     QVERIFY(canvas.rootObject() != 0);
1879
1880     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1881
1882     QVERIFY(input != 0);
1883     QTRY_VERIFY(input->hasActiveFocus() == true);
1884     QVERIFY(input->isReadOnly() == true);
1885     QString initial = input->text();
1886     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1887         simulateKey(&canvas, k);
1888     simulateKey(&canvas, Qt::Key_Return);
1889     simulateKey(&canvas, Qt::Key_Space);
1890     simulateKey(&canvas, Qt::Key_Escape);
1891     QCOMPARE(input->text(), initial);
1892
1893     input->setCursorPosition(3);
1894     input->setReadOnly(false);
1895     QCOMPARE(input->isReadOnly(), false);
1896     QCOMPARE(input->cursorPosition(), input->text().length());
1897 }
1898
1899 void tst_qquicktextinput::echoMode()
1900 {
1901     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1902     canvas.show();
1903     canvas.requestActivateWindow();
1904     QTest::qWaitForWindowShown(&canvas);
1905     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1906
1907     QVERIFY(canvas.rootObject() != 0);
1908
1909     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1910
1911     QVERIFY(input != 0);
1912     QTRY_VERIFY(input->hasActiveFocus() == true);
1913     QString initial = input->text();
1914     Qt::InputMethodHints ref;
1915     QCOMPARE(initial, QLatin1String("ABCDefgh"));
1916     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
1917     QCOMPARE(input->displayText(), input->text());
1918     //Normal
1919     ref &= ~Qt::ImhHiddenText;
1920     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1921     QCOMPARE(input->inputMethodHints(), ref);
1922     input->setEchoMode(QQuickTextInput::NoEcho);
1923     QCOMPARE(input->text(), initial);
1924     QCOMPARE(input->displayText(), QLatin1String(""));
1925     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1926     //NoEcho
1927     ref |= Qt::ImhHiddenText;
1928     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1929     QCOMPARE(input->inputMethodHints(), ref);
1930     input->setEchoMode(QQuickTextInput::Password);
1931     //Password
1932     ref |= Qt::ImhHiddenText;
1933     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1934     QCOMPARE(input->text(), initial);
1935     QCOMPARE(input->displayText(), QLatin1String("********"));
1936     QCOMPARE(input->inputMethodHints(), ref);
1937     input->setPasswordCharacter(QChar('Q'));
1938     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
1939     QCOMPARE(input->text(), initial);
1940     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1941     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
1942     //PasswordEchoOnEdit
1943     ref &= ~Qt::ImhHiddenText;
1944     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1945     QCOMPARE(input->inputMethodHints(), ref);
1946     QCOMPARE(input->text(), initial);
1947     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1948     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
1949     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
1950     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1951     QCOMPARE(input->text(), QLatin1String("a"));
1952     QCOMPARE(input->displayText(), QLatin1String("a"));
1953     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
1954     input->setFocus(false);
1955     QVERIFY(input->hasActiveFocus() == false);
1956     QCOMPARE(input->displayText(), QLatin1String("Q"));
1957     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
1958     input->setFocus(true);
1959     QVERIFY(input->hasActiveFocus());
1960     QInputMethodEvent inputEvent;
1961     inputEvent.setCommitString(initial);
1962     QGuiApplication::sendEvent(input, &inputEvent);
1963     QCOMPARE(input->text(), initial);
1964     QCOMPARE(input->displayText(), initial);
1965     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1966 }
1967
1968 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1969 void tst_qdeclarativetextinput::passwordEchoDelay()
1970 {
1971     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1972     canvas.show();
1973     canvas.setFocus();
1974     QGuiApplication::setActiveWindow(&canvas);
1975     QTest::qWaitForWindowShown(&canvas);
1976     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1977
1978     QVERIFY(canvas.rootObject() != 0);
1979
1980     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1981
1982     QChar fillChar = QLatin1Char('*');
1983
1984     input->setEchoMode(QDeclarativeTextInput::Password);
1985     QCOMPARE(input->displayText(), QString(8, fillChar));
1986     input->setText(QString());
1987     QCOMPARE(input->displayText(), QString());
1988
1989     QTest::keyPress(&canvas, '0');
1990     QTest::keyPress(&canvas, '1');
1991     QTest::keyPress(&canvas, '2');
1992     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1993     QTest::keyPress(&canvas, '3');
1994     QTest::keyPress(&canvas, '4');
1995     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1996     QTest::keyPress(&canvas, Qt::Key_Backspace);
1997     QCOMPARE(input->displayText(), QString(4, fillChar));
1998     QTest::keyPress(&canvas, '4');
1999     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2000     QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2001     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2002     QTest::keyPress(&canvas, '5');
2003     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2004     input->setFocus(false);
2005     QVERIFY(!input->hasFocus());
2006     QCOMPARE(input->displayText(), QString(6, fillChar));
2007     input->setFocus(true);
2008     QTRY_VERIFY(input->hasFocus());
2009     QCOMPARE(input->displayText(), QString(6, fillChar));
2010     QTest::keyPress(&canvas, '6');
2011     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2012
2013     QInputMethodEvent ev;
2014     ev.setCommitString(QLatin1String("7"));
2015     QGuiApplication::sendEvent(&canvas, &ev);
2016     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2017 }
2018 #endif
2019
2020
2021 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2022 {
2023     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2024     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2025
2026     QGuiApplication::sendEvent(view, &press);
2027     QGuiApplication::sendEvent(view, &release);
2028 }
2029
2030 #ifndef QTBUG_21691
2031 class MyInputContext : public QInputContext
2032 {
2033 public:
2034     MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
2035     ~MyInputContext() {}
2036
2037     QString identifierName() { return QString(); }
2038     QString language() { return QString(); }
2039
2040     void reset() {}
2041
2042     bool isComposing() const { return false; }
2043
2044     void update() { updateReceived = true; }
2045
2046     void mouseHandler(int x, QMouseEvent *event)
2047     {
2048         cursor = x;
2049         eventType = event->type();
2050         eventPosition = event->pos();
2051         eventGlobalPosition = event->globalPos();
2052         eventButton = event->button();
2053         eventButtons = event->buttons();
2054         eventModifiers = event->modifiers();
2055     }
2056
2057     void sendPreeditText(const QString &text, int cursor)
2058     {
2059         QList<QInputMethodEvent::Attribute> attributes;
2060         attributes.append(QInputMethodEvent::Attribute(
2061                 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2062
2063         QInputMethodEvent event(text, attributes);
2064         sendEvent(event);
2065     }
2066
2067     bool updateReceived;
2068     int cursor;
2069     QEvent::Type eventType;
2070     QPoint eventPosition;
2071     QPoint eventGlobalPosition;
2072     Qt::MouseButton eventButton;
2073     Qt::MouseButtons eventButtons;
2074     Qt::KeyboardModifiers eventModifiers;
2075 };
2076 #endif
2077
2078 void tst_qquicktextinput::openInputPanel()
2079 {
2080     QQuickView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2081     view.show();
2082     view.requestActivateWindow();
2083     QTest::qWaitForWindowShown(&view);
2084     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2085
2086     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2087     QVERIFY(input);
2088
2089     // check default values
2090     QVERIFY(input->focusOnPress());
2091     QVERIFY(!input->hasActiveFocus());
2092     qDebug() << &input << qApp->inputPanel()->inputItem();
2093     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2094     QEXPECT_FAIL("", "QTBUG-21946", Abort);
2095     QCOMPARE(qApp->inputPanel()->visible(), false);
2096
2097     // input panel should open on focus
2098     QPoint centerPoint(view.width()/2, view.height()/2);
2099     Qt::KeyboardModifiers noModifiers = 0;
2100     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2101     QGuiApplication::processEvents();
2102     QVERIFY(input->hasActiveFocus());
2103     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2104     QCOMPARE(qApp->inputPanel()->visible(), true);
2105     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2106
2107     // input panel should be re-opened when pressing already focused TextInput
2108     qApp->inputPanel()->hide();
2109     QCOMPARE(qApp->inputPanel()->visible(), false);
2110     QVERIFY(input->hasActiveFocus());
2111     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2112     QGuiApplication::processEvents();
2113     QCOMPARE(qApp->inputPanel()->visible(), true);
2114     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2115
2116     // input panel should stay visible if focus is lost to another text inputor
2117     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2118     QQuickTextInput anotherInput;
2119     anotherInput.setParentItem(view.rootObject());
2120     anotherInput.setFocus(true);
2121     QCOMPARE(qApp->inputPanel()->visible(), true);
2122     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2123     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2124
2125     anotherInput.setFocus(false);
2126     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2127     QCOMPARE(view.activeFocusItem(), view.rootItem());
2128     anotherInput.setFocus(true);
2129
2130     // input item should be null if focus is lost to an item that doesn't accept inputs
2131     QQuickItem item;
2132     item.setParentItem(view.rootObject());
2133     item.setFocus(true);
2134     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2135     QCOMPARE(view.activeFocusItem(), &item);
2136
2137     qApp->inputPanel()->hide();
2138
2139     // input panel should not be opened if TextInput is read only
2140     input->setReadOnly(true);
2141     input->setFocus(true);
2142     QCOMPARE(qApp->inputPanel()->visible(), false);
2143     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2144     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2145     QGuiApplication::processEvents();
2146     QCOMPARE(qApp->inputPanel()->visible(), false);
2147
2148     // input panel should not be opened if focusOnPress is set to false
2149     input->setFocusOnPress(false);
2150     input->setFocus(false);
2151     input->setFocus(true);
2152     QCOMPARE(qApp->inputPanel()->visible(), false);
2153     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2154     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2155     QCOMPARE(qApp->inputPanel()->visible(), false);
2156
2157     // input panel should open when openSoftwareInputPanel is called
2158     input->openSoftwareInputPanel();
2159     QCOMPARE(qApp->inputPanel()->visible(), true);
2160
2161     // input panel should close when closeSoftwareInputPanel is called
2162     input->closeSoftwareInputPanel();
2163     QCOMPARE(qApp->inputPanel()->visible(), false);
2164 }
2165
2166 class MyTextInput : public QQuickTextInput
2167 {
2168 public:
2169     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2170     {
2171         nbPaint = 0;
2172     }
2173     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2174     {
2175        nbPaint++;
2176        return QQuickTextInput::updatePaintNode(node, data);
2177     }
2178     int nbPaint;
2179 };
2180
2181 void tst_qquicktextinput::setHAlignClearCache()
2182 {
2183     QQuickView view;
2184     MyTextInput input;
2185     input.setText("Hello world");
2186     input.setParentItem(view.rootItem());
2187     view.show();
2188     view.requestActivateWindow();
2189     QTest::qWaitForWindowShown(&view);
2190     QTRY_COMPARE(input.nbPaint, 1);
2191     input.setHAlign(QQuickTextInput::AlignRight);
2192     //Changing the alignment should trigger a repaint
2193     QTRY_COMPARE(input.nbPaint, 2);
2194 }
2195
2196 void tst_qquicktextinput::focusOutClearSelection()
2197 {
2198     QQuickView view;
2199     QQuickTextInput input;
2200     QQuickTextInput input2;
2201     input.setText(QLatin1String("Hello world"));
2202     input.setFocus(true);
2203     input2.setParentItem(view.rootItem());
2204     input.setParentItem(view.rootItem());
2205     view.show();
2206     view.requestActivateWindow();
2207     QTest::qWaitForWindowShown(&view);
2208     input.select(2,5);
2209     //The selection should work
2210     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2211     input2.setFocus(true);
2212     QGuiApplication::processEvents();
2213     //The input lost the focus selection should be cleared
2214     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2215 }
2216
2217 void tst_qquicktextinput::geometrySignals()
2218 {
2219     QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2220     QObject *o = component.create();
2221     QVERIFY(o);
2222     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2223     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2224     delete o;
2225 }
2226
2227 void tst_qquicktextinput::testQtQuick11Attributes()
2228 {
2229     QFETCH(QString, code);
2230     QFETCH(QString, warning);
2231     QFETCH(QString, error);
2232
2233     QDeclarativeEngine engine;
2234     QObject *obj;
2235
2236     QDeclarativeComponent valid(&engine);
2237     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2238     obj = valid.create();
2239     QVERIFY(obj);
2240     QVERIFY(valid.errorString().isEmpty());
2241     delete obj;
2242
2243     QDeclarativeComponent invalid(&engine);
2244     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2245     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2246     obj = invalid.create();
2247     QCOMPARE(invalid.errorString(), error);
2248     delete obj;
2249 }
2250
2251 void tst_qquicktextinput::testQtQuick11Attributes_data()
2252 {
2253     QTest::addColumn<QString>("code");
2254     QTest::addColumn<QString>("warning");
2255     QTest::addColumn<QString>("error");
2256
2257     QTest::newRow("canPaste") << "property bool foo: canPaste"
2258         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2259         << "";
2260
2261     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2262         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2263         << "";
2264
2265     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2266         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2267         << "";
2268 }
2269
2270 void tst_qquicktextinput::preeditAutoScroll()
2271 {
2272 #ifdef QTBUG_21691
2273     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2274     QVERIFY(false);
2275 #else
2276     QString preeditText = "califragisiticexpialidocious!";
2277
2278     QQuickView view(QUrl::fromLocalFile(TESTDATA("preeditAutoScroll.qml")));
2279     MyInputContext ic;
2280     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2281     // and QWidget won't allow an input context to be set when the flag is not set.
2282     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2283     view.setInputContext(&ic);
2284     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2285     view.show();
2286     view.requestActivateWindow();
2287     QTest::qWaitForWindowShown(&view);
2288     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2289     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2290     QVERIFY(input);
2291
2292     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2293     int cursorRectangleChanges = 0;
2294
2295     QFontMetricsF fm(input->font());
2296     input->setWidth(fm.width(input->text()));
2297
2298     // test the text is scrolled so the preedit is visible.
2299     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2300     QVERIFY(input->positionAt(0) != 0);
2301     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2302     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2303
2304     // test the text is scrolled back when the preedit is removed.
2305     ic.sendEvent(QInputMethodEvent());
2306     QCOMPARE(input->positionAt(0), 0);
2307     QCOMPARE(input->positionAt(input->width()), 5);
2308     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2309
2310     // some tolerance for different fonts.
2311 #ifdef Q_OS_LINUX
2312     const int error = 2;
2313 #else
2314     const int error = 5;
2315 #endif
2316
2317     // test if the preedit is larger than the text input that the
2318     // character preceding the cursor is still visible.
2319     qreal x = input->positionToRectangle(0).x();
2320     for (int i = 0; i < 3; ++i) {
2321         ic.sendPreeditText(preeditText, i + 1);
2322         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2323         QVERIFY(input->positionToRectangle(0).x() < x);
2324         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2325         x = input->positionToRectangle(0).x();
2326     }
2327     for (int i = 1; i >= 0; --i) {
2328         ic.sendPreeditText(preeditText, i + 1);
2329         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2330         QVERIFY(input->positionToRectangle(0).x() > x);
2331         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2332         x = input->positionToRectangle(0).x();
2333     }
2334
2335     // Test incrementing the preedit cursor doesn't cause further
2336     // scrolling when right most text is visible.
2337     ic.sendPreeditText(preeditText, preeditText.length() - 3);
2338     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2339     x = input->positionToRectangle(0).x();
2340     for (int i = 2; i >= 0; --i) {
2341         ic.sendPreeditText(preeditText, preeditText.length() - i);
2342         QCOMPARE(input->positionToRectangle(0).x(), x);
2343         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2344     }
2345     for (int i = 1; i <  3; ++i) {
2346         ic.sendPreeditText(preeditText, preeditText.length() - i);
2347         QCOMPARE(input->positionToRectangle(0).x(), x);
2348         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2349     }
2350
2351     // Test disabling auto scroll.
2352     ic.sendEvent(QInputMethodEvent());
2353
2354     input->setAutoScroll(false);
2355     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2356     QCOMPARE(input->positionAt(0), 0);
2357     QCOMPARE(input->positionAt(input->width()), 5);
2358 #endif
2359 }
2360
2361 void tst_qquicktextinput::preeditMicroFocus()
2362 {
2363 #ifdef QTBUG_21691
2364     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2365     QVERIFY(false);
2366 #else
2367     QString preeditText = "super";
2368
2369     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2370     MyInputContext ic;
2371     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2372     // and QWidget won't allow an input context to be set when the flag is not set.
2373     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2374     view.setInputContext(&ic);
2375     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2376     view.show();
2377     view.requestActivateWindow();
2378     QTest::qWaitForWindowShown(&view);
2379     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2380     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2381     QVERIFY(input);
2382
2383     QRect currentRect;
2384     QRect previousRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2385
2386     // Verify that the micro focus rect is positioned the same for position 0 as
2387     // it would be if there was no preedit text.
2388     ic.updateReceived = false;
2389     ic.sendPreeditText(preeditText, 0);
2390     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2391     QCOMPARE(currentRect, previousRect);
2392     QCOMPARE(ic.updateReceived, true);
2393
2394     // Verify that the micro focus rect moves to the left as the cursor position
2395     // is incremented.
2396     for (int i = 1; i <= 5; ++i) {
2397         ic.updateReceived = false;
2398         ic.sendPreeditText(preeditText, i);
2399         currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2400         QVERIFY(previousRect.left() < currentRect.left());
2401
2402         QCOMPARE(ic.updateReceived, true);
2403         previousRect = currentRect;
2404     }
2405
2406     // Verify that if there is no preedit cursor then the micro focus rect is the
2407     // same as it would be if it were positioned at the end of the preedit text.
2408     ic.sendPreeditText(preeditText, 0);
2409     ic.updateReceived = false;
2410     ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2411     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2412     QCOMPARE(currentRect, previousRect);
2413     QCOMPARE(ic.updateReceived, true);
2414 #endif
2415 }
2416
2417 void tst_qquicktextinput::inputContextMouseHandler()
2418 {
2419 #ifdef QTBUG_21691
2420     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2421     QVERIFY(false);
2422 #else
2423     QString text = "supercalifragisiticexpialidocious!";
2424
2425     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2426     MyInputContext ic;
2427     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2428     // and QWidget won't allow an input context to be set when the flag is not set.
2429     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2430     view.setInputContext(&ic);
2431     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2432     view.show();
2433     view.requestActivateWindow();
2434     QTest::qWaitForWindowShown(&view);
2435     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2436     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2437     QVERIFY(input);
2438
2439     QFontMetricsF fm(input->font());
2440     const qreal y = fm.height() / 2;
2441
2442     QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2443     QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2444     QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2445     QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2446     QPoint globalPosition2 = view.mapToGlobal(position2);
2447     QPoint globalposition8 = view.mapToGlobal(position8);
2448     QPoint globalposition20 = view.mapToGlobal(position20);
2449     QPoint globalposition27 = view.mapToGlobal(position27);
2450
2451     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2452
2453     QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2454     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2455     QCOMPARE(ic.eventPosition, position2);
2456     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2457     QCOMPARE(ic.eventButton, Qt::LeftButton);
2458     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2459     QVERIFY(ic.cursor < 0);
2460     ic.eventType = QEvent::None;
2461
2462     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2463     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2464     QCOMPARE(ic.eventPosition, position2);
2465     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2466     QCOMPARE(ic.eventButton, Qt::LeftButton);
2467     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2468     QVERIFY(ic.cursor < 0);
2469     ic.eventType = QEvent::None;
2470
2471     {   QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2472         QGuiApplication::sendEvent(&view, &mv); }
2473     QCOMPARE(ic.eventType, QEvent::None);
2474
2475     {   QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2476         QGuiApplication::sendEvent(&view, &mv); }
2477     QCOMPARE(ic.eventType, QEvent::MouseMove);
2478     QCOMPARE(ic.eventPosition, position27);
2479     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2480     QCOMPARE(ic.eventButton, Qt::LeftButton);
2481     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2482     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);    // 15 is expected but some platforms may be off by one.
2483     ic.eventType = QEvent::None;
2484
2485     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2486     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2487     QCOMPARE(ic.eventPosition, position27);
2488     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2489     QCOMPARE(ic.eventButton, Qt::LeftButton);
2490     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2491     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2492     ic.eventType = QEvent::None;
2493
2494     // And in the other direction.
2495     QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2496     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2497     QCOMPARE(ic.eventPosition, position27);
2498     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2499     QCOMPARE(ic.eventButton, Qt::LeftButton);
2500     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2501     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2502     ic.eventType = QEvent::None;
2503
2504     QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2505     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2506     QCOMPARE(ic.eventPosition, position27);
2507     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2508     QCOMPARE(ic.eventButton, Qt::RightButton);
2509     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2510     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2511     ic.eventType = QEvent::None;
2512
2513     {   QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2514         QGuiApplication::sendEvent(&view, &mv); }
2515     QCOMPARE(ic.eventType, QEvent::MouseMove);
2516     QCOMPARE(ic.eventPosition, position20);
2517     QCOMPARE(ic.eventGlobalPosition, globalposition20);
2518     QCOMPARE(ic.eventButton, Qt::RightButton);
2519     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2520     QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2521     ic.eventType = QEvent::None;
2522
2523     {   QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2524         QGuiApplication::sendEvent(&view, &mv); }
2525     QCOMPARE(ic.eventType, QEvent::None);
2526
2527     QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2528     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2529     QCOMPARE(ic.eventPosition, position2);
2530     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2531     QCOMPARE(ic.eventButton, Qt::RightButton);
2532     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2533     QVERIFY(ic.cursor < 0);
2534     ic.eventType = QEvent::None;
2535 #endif
2536 }
2537
2538 void tst_qquicktextinput::inputMethodComposing()
2539 {
2540     QString text = "supercalifragisiticexpialidocious!";
2541
2542     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2543     view.show();
2544     view.requestActivateWindow();
2545     QTest::qWaitForWindowShown(&view);
2546     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2547     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2548     QVERIFY(input);
2549     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2550
2551     QCOMPARE(input->isInputMethodComposing(), false);
2552     {
2553         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2554         QGuiApplication::sendEvent(input, &event);
2555     }
2556     QCOMPARE(input->isInputMethodComposing(), true);
2557     QCOMPARE(spy.count(), 1);
2558
2559     {
2560         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2561         QGuiApplication::sendEvent(input, &event);
2562     }
2563     QCOMPARE(spy.count(), 1);
2564
2565     {
2566         QInputMethodEvent event;
2567         QGuiApplication::sendEvent(input, &event);
2568     }
2569     QCOMPARE(input->isInputMethodComposing(), false);
2570     QCOMPARE(spy.count(), 2);
2571 }
2572
2573 void tst_qquicktextinput::cursorRectangleSize()
2574 {
2575     QQuickView *canvas = new QQuickView(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
2576     QVERIFY(canvas->rootObject() != 0);
2577     canvas->show();
2578     canvas->requestActivateWindow();
2579     QTest::qWaitForWindowShown(canvas);
2580
2581     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2582     QVERIFY(textInput != 0);
2583     textInput->setFocus(Qt::OtherFocusReason);
2584     QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2585     QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2586     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2587     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2588
2589     QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2590
2591     QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2592     QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2593
2594     delete canvas;
2595 }
2596
2597 void tst_qquicktextinput::tripleClickSelectsAll()
2598 {
2599     QString qmlfile = TESTDATA("positionAt.qml");
2600     QQuickView view(QUrl::fromLocalFile(qmlfile));
2601     view.show();
2602     view.requestActivateWindow();
2603     QTest::qWaitForWindowShown(&view);
2604
2605     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2606
2607     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2608     QVERIFY(input);
2609
2610     QLatin1String hello("Hello world!");
2611     input->setSelectByMouse(true);
2612     input->setText(hello);
2613
2614     // Clicking on the same point inside TextInput three times in a row
2615     // should trigger a triple click, thus selecting all the text.
2616     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2617     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2618     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2619     QGuiApplication::processEvents();
2620     QCOMPARE(input->selectedText(), hello);
2621
2622     // Now it simulates user moving the mouse between the second and the third click.
2623     // In this situation, we don't expect a triple click.
2624     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2625     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2626     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2627     QGuiApplication::processEvents();
2628     QVERIFY(input->selectedText().isEmpty());
2629
2630     // And now we press the third click too late, so no triple click event is triggered.
2631     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2632     QGuiApplication::processEvents();
2633     QTest::qWait(QApplication::doubleClickInterval() + 1);
2634     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2635     QGuiApplication::processEvents();
2636     QVERIFY(input->selectedText().isEmpty());
2637 }
2638
2639 void tst_qquicktextinput::QTBUG_19956_data()
2640 {
2641     QTest::addColumn<QString>("url");
2642     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2643     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2644 }
2645
2646 void tst_qquicktextinput::QTBUG_19956()
2647 {
2648     QFETCH(QString, url);
2649
2650     QQuickView canvas(QUrl::fromLocalFile(TESTDATA(url)));
2651     canvas.show();
2652     canvas.requestActivateWindow();
2653     QTest::qWaitForWindowShown(&canvas);
2654     QVERIFY(canvas.rootObject() != 0);
2655     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2656     QVERIFY(input);
2657     input->setFocus(true);
2658     QVERIFY(input->hasActiveFocus());
2659
2660     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
2661     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2662     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
2663     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2664
2665     canvas.rootObject()->setProperty("topvalue", 15);
2666     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
2667     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2668
2669     canvas.rootObject()->setProperty("topvalue", 25);
2670     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
2671     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2672
2673     canvas.rootObject()->setProperty("bottomvalue", 21);
2674     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
2675     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2676
2677     canvas.rootObject()->setProperty("bottomvalue", 10);
2678     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2679     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2680 }
2681
2682 void tst_qquicktextinput::QTBUG_19956_regexp()
2683 {
2684     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("qtbug-19956regexp.qml")));
2685     canvas.show();
2686     canvas.requestActivateWindow();
2687     QTest::qWaitForWindowShown(&canvas);
2688     QVERIFY(canvas.rootObject() != 0);
2689     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2690     QVERIFY(input);
2691     input->setFocus(true);
2692     QVERIFY(input->hasActiveFocus());
2693
2694     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2695     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2696     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
2697     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2698
2699     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
2700     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
2701     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2702
2703     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2704     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2705     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2706 }
2707
2708 QTEST_MAIN(tst_qquicktextinput)
2709
2710 #include "tst_qquicktextinput.moc"