23224d0452da5e03de6526509ca631f4325f02d5
[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 #ifndef Q_OS_MAC    // QTBUG-18040
1168     // empty text with implicit alignment follows the system locale-based
1169     // keyboard input direction from QGuiApplication::keyboardInputDirection
1170     textInput->setText("");
1171     QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1172                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1173     if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1174         QVERIFY(-textInputPrivate->hscroll < canvas.width()/2);
1175     else
1176         QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1177     textInput->setHAlign(QQuickTextInput::AlignRight);
1178     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1179     QVERIFY(-textInputPrivate->hscroll > canvas.width()/2);
1180 #endif
1181
1182 #ifndef Q_OS_MAC    // QTBUG-18040
1183     // alignment of TextInput with no text set to it
1184     QString componentStr = "import QtQuick 2.0\nTextInput {}";
1185     QDeclarativeComponent textComponent(&engine);
1186     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1187     QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1188     QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1189                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1190     delete textObject;
1191 #endif
1192 }
1193
1194 void tst_qquicktextinput::positionAt()
1195 {
1196     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1197     QVERIFY(canvas.rootObject() != 0);
1198     canvas.show();
1199     canvas.requestActivateWindow();
1200     QTest::qWaitForWindowShown(&canvas);
1201
1202     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1203     QVERIFY(textinputObject != 0);
1204
1205     // Check autoscrolled...
1206     QFontMetrics fm(textinputObject->font());
1207
1208     int pos = textinputObject->positionAt(textinputObject->width()/2);
1209     int textWidth = 0;
1210     int textLeftWidth = 0;
1211     if (!qmlDisableDistanceField()) {
1212         {
1213             QTextLayout layout(textinputObject->text().left(pos));
1214
1215             {
1216                 QTextOption option;
1217                 option.setUseDesignMetrics(true);
1218                 layout.setTextOption(option);
1219             }
1220
1221             layout.beginLayout();
1222             QTextLine line = layout.createLine();
1223             layout.endLayout();
1224
1225             textLeftWidth = ceil(line.horizontalAdvance());
1226         }
1227         {
1228             QTextLayout layout(textinputObject->text());
1229
1230             {
1231                 QTextOption option;
1232                 option.setUseDesignMetrics(true);
1233                 layout.setTextOption(option);
1234             }
1235
1236             layout.beginLayout();
1237             QTextLine line = layout.createLine();
1238             layout.endLayout();
1239
1240             textWidth = ceil(line.horizontalAdvance());
1241         }
1242     } else {
1243         textWidth = fm.width(textinputObject->text());
1244         textLeftWidth = fm.width(textinputObject->text().left(pos));
1245     }
1246
1247     int diff = abs(textWidth - (textLeftWidth+textinputObject->width()/2));
1248
1249     // some tollerance for different fonts.
1250     QEXPECT_FAIL("", "QTBUG-21689", Abort);
1251 #ifdef Q_OS_LINUX
1252     QVERIFY(diff < 2);
1253 #else
1254     QVERIFY(diff < 5);
1255 #endif
1256
1257     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1258     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1259     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1260
1261     // Check without autoscroll...
1262     textinputObject->setAutoScroll(false);
1263     pos = textinputObject->positionAt(textinputObject->width()/2);
1264
1265     if (!qmlDisableDistanceField()) {
1266         {
1267             QTextLayout layout(textinputObject->text().left(pos));
1268
1269             {
1270                 QTextOption option;
1271                 option.setUseDesignMetrics(true);
1272                 layout.setTextOption(option);
1273             }
1274
1275             layout.beginLayout();
1276             QTextLine line = layout.createLine();
1277             layout.endLayout();
1278
1279             textLeftWidth = ceil(line.horizontalAdvance());
1280         }
1281     } else {
1282         textLeftWidth = fm.width(textinputObject->text().left(pos));
1283     }
1284
1285     diff = abs(int(textLeftWidth-textinputObject->width()/2));
1286
1287     // some tollerance for different fonts.
1288 #ifdef Q_OS_LINUX
1289     QVERIFY(diff < 2);
1290 #else
1291     QVERIFY(diff < 5);
1292 #endif
1293
1294     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1295     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorBetweenCharacters), pos + 1);
1296     QCOMPARE(textinputObject->positionAt(x, QQuickTextInput::CursorOnCharacter), pos);
1297
1298     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1299     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1300
1301     QString preeditText = textinputObject->text().mid(0, pos);
1302     textinputObject->setText(textinputObject->text().mid(pos));
1303     textinputObject->setCursorPosition(0);
1304
1305     QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1306     QGuiApplication::sendEvent(&canvas, &inputEvent);
1307
1308     // Check all points within the preedit text return the same position.
1309     QCOMPARE(textinputObject->positionAt(0), 0);
1310     QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1311     QCOMPARE(textinputObject->positionAt(x0), 0);
1312
1313     // Verify positioning returns to normal after the preedit text.
1314     QCOMPARE(textinputObject->positionAt(x1), 1);
1315     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1316 }
1317
1318 void tst_qquicktextinput::maxLength()
1319 {
1320     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("maxLength.qml")));
1321     QVERIFY(canvas.rootObject() != 0);
1322     canvas.show();
1323     canvas.requestActivateWindow();
1324     QTest::qWaitForWindowShown(&canvas);
1325
1326     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1327     QVERIFY(textinputObject != 0);
1328     QVERIFY(textinputObject->text().isEmpty());
1329     QVERIFY(textinputObject->maxLength() == 10);
1330     foreach (const QString &str, standard) {
1331         QVERIFY(textinputObject->text().length() <= 10);
1332         textinputObject->setText(str);
1333         QVERIFY(textinputObject->text().length() <= 10);
1334     }
1335
1336     textinputObject->setText("");
1337     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1338     for (int i=0; i<20; i++) {
1339         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1340         //simulateKey(&canvas, Qt::Key_A);
1341         QTest::keyPress(&canvas, Qt::Key_A);
1342         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1343         QTest::qWait(50);
1344     }
1345 }
1346
1347 void tst_qquicktextinput::masks()
1348 {
1349     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1350     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1351     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("masks.qml")));
1352     canvas.show();
1353     canvas.requestActivateWindow();
1354     QVERIFY(canvas.rootObject() != 0);
1355     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1356     QVERIFY(textinputObject != 0);
1357     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1358     QVERIFY(textinputObject->text().length() == 0);
1359     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1360     for (int i=0; i<10; i++) {
1361         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1362         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1363         //simulateKey(&canvas, Qt::Key_A);
1364         QTest::keyPress(&canvas, Qt::Key_A);
1365         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1366         QTest::qWait(50);
1367     }
1368 }
1369
1370 void tst_qquicktextinput::validators()
1371 {
1372     // Note that this test assumes that the validators are working properly
1373     // so you may need to run their tests first. All validators are checked
1374     // here to ensure that their exposure to QML is working.
1375
1376     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("validators.qml")));
1377     canvas.show();
1378     canvas.requestActivateWindow();
1379
1380     QVERIFY(canvas.rootObject() != 0);
1381
1382     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1383     QVERIFY(intInput);
1384     intInput->setFocus(true);
1385     QTRY_VERIFY(intInput->hasActiveFocus());
1386     QTest::keyPress(&canvas, Qt::Key_1);
1387     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1388     QTest::qWait(50);
1389     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1390     QCOMPARE(intInput->hasAcceptableInput(), false);
1391     QTest::keyPress(&canvas, Qt::Key_2);
1392     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1393     QTest::qWait(50);
1394     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1395     QCOMPARE(intInput->hasAcceptableInput(), false);
1396     QTest::keyPress(&canvas, Qt::Key_1);
1397     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1398     QTest::qWait(50);
1399     QCOMPARE(intInput->text(), QLatin1String("11"));
1400     QCOMPARE(intInput->hasAcceptableInput(), true);
1401     QTest::keyPress(&canvas, Qt::Key_0);
1402     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1403     QTest::qWait(50);
1404     QCOMPARE(intInput->text(), QLatin1String("11"));
1405     QCOMPARE(intInput->hasAcceptableInput(), true);
1406
1407     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1408     QTRY_VERIFY(dblInput);
1409     dblInput->setFocus(true);
1410     QVERIFY(dblInput->hasActiveFocus() == true);
1411     QTest::keyPress(&canvas, Qt::Key_1);
1412     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1413     QTest::qWait(50);
1414     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1415     QCOMPARE(dblInput->hasAcceptableInput(), false);
1416     QTest::keyPress(&canvas, Qt::Key_2);
1417     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1418     QTest::qWait(50);
1419     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1420     QCOMPARE(dblInput->hasAcceptableInput(), true);
1421     QTest::keyPress(&canvas, Qt::Key_Period);
1422     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1423     QTest::qWait(50);
1424     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1425     QCOMPARE(dblInput->hasAcceptableInput(), true);
1426     QTest::keyPress(&canvas, Qt::Key_1);
1427     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1428     QTest::qWait(50);
1429     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1430     QCOMPARE(dblInput->hasAcceptableInput(), true);
1431     QTest::keyPress(&canvas, Qt::Key_1);
1432     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1433     QTest::qWait(50);
1434     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1435     QCOMPARE(dblInput->hasAcceptableInput(), true);
1436     QTest::keyPress(&canvas, Qt::Key_1);
1437     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1438     QTest::qWait(50);
1439     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1440     QCOMPARE(dblInput->hasAcceptableInput(), true);
1441
1442     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1443     QTRY_VERIFY(strInput);
1444     strInput->setFocus(true);
1445     QVERIFY(strInput->hasActiveFocus() == true);
1446     QTest::keyPress(&canvas, Qt::Key_1);
1447     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1448     QTest::qWait(50);
1449     QTRY_COMPARE(strInput->text(), QLatin1String(""));
1450     QCOMPARE(strInput->hasAcceptableInput(), false);
1451     QTest::keyPress(&canvas, Qt::Key_A);
1452     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1453     QTest::qWait(50);
1454     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1455     QCOMPARE(strInput->hasAcceptableInput(), false);
1456     QTest::keyPress(&canvas, Qt::Key_A);
1457     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1458     QTest::qWait(50);
1459     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1460     QCOMPARE(strInput->hasAcceptableInput(), true);
1461     QTest::keyPress(&canvas, Qt::Key_A);
1462     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1463     QTest::qWait(50);
1464     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1465     QCOMPARE(strInput->hasAcceptableInput(), true);
1466     QTest::keyPress(&canvas, Qt::Key_A);
1467     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1468     QTest::qWait(50);
1469     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1470     QCOMPARE(strInput->hasAcceptableInput(), true);
1471     QTest::keyPress(&canvas, Qt::Key_A);
1472     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1473     QTest::qWait(50);
1474     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1475     QCOMPARE(strInput->hasAcceptableInput(), true);
1476 }
1477
1478 void tst_qquicktextinput::inputMethods()
1479 {
1480     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("inputmethods.qml")));
1481     canvas.show();
1482     canvas.requestActivateWindow();
1483     QTest::qWaitForWindowShown(&canvas);
1484
1485     // test input method hints
1486     QVERIFY(canvas.rootObject() != 0);
1487     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1488     QVERIFY(input != 0);
1489     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1490     input->setInputMethodHints(Qt::ImhUppercaseOnly);
1491     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1492
1493     input->setFocus(true);
1494     QVERIFY(input->hasActiveFocus() == true);
1495     // test that input method event is committed
1496     QInputMethodEvent event;
1497     event.setCommitString( "My ", -12, 0);
1498     QGuiApplication::sendEvent(&canvas, &event);
1499     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
1500     QCOMPARE(input->text(), QString("My Hello world!"));
1501
1502     input->setCursorPosition(2);
1503     event.setCommitString("Your", -2, 2);
1504     QGuiApplication::sendEvent(&canvas, &event);
1505     QCOMPARE(input->text(), QString("Your Hello world!"));
1506     QCOMPARE(input->cursorPosition(), 4);
1507
1508     input->setCursorPosition(7);
1509     event.setCommitString("Goodbye", -2, 5);
1510     QGuiApplication::sendEvent(&canvas, &event);
1511     QCOMPARE(input->text(), QString("Your Goodbye world!"));
1512     QCOMPARE(input->cursorPosition(), 12);
1513
1514     input->setCursorPosition(8);
1515     event.setCommitString("Our", -8, 4);
1516     QGuiApplication::sendEvent(&canvas, &event);
1517     QCOMPARE(input->text(), QString("Our Goodbye world!"));
1518     QCOMPARE(input->cursorPosition(), 7);
1519 }
1520
1521 /*
1522 TextInput element should only handle left/right keys until the cursor reaches
1523 the extent of the text, then they should ignore the keys.
1524
1525 */
1526 void tst_qquicktextinput::navigation()
1527 {
1528     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1529     canvas.show();
1530     canvas.requestActivateWindow();
1531
1532     QVERIFY(canvas.rootObject() != 0);
1533
1534     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1535
1536     QVERIFY(input != 0);
1537     input->setCursorPosition(0);
1538     QTRY_VERIFY(input->hasActiveFocus() == true);
1539     simulateKey(&canvas, Qt::Key_Left);
1540     QVERIFY(input->hasActiveFocus() == false);
1541     simulateKey(&canvas, Qt::Key_Right);
1542     QVERIFY(input->hasActiveFocus() == true);
1543     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1544     input->setCursorPosition(input->text().length());
1545     input->select(0,input->text().length());
1546     QVERIFY(input->selectionStart() != input->selectionEnd());
1547     simulateKey(&canvas, Qt::Key_Right);
1548     QVERIFY(input->selectionStart() == input->selectionEnd());
1549     QVERIFY(input->selectionStart() == input->text().length());
1550     QVERIFY(input->hasActiveFocus() == true);
1551     simulateKey(&canvas, Qt::Key_Right);
1552     QVERIFY(input->hasActiveFocus() == false);
1553     simulateKey(&canvas, Qt::Key_Left);
1554     QVERIFY(input->hasActiveFocus() == true);
1555
1556     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1557     input->setCursorPosition(2);
1558     QCOMPARE(input->cursorPosition(),2);
1559     simulateKey(&canvas, Qt::Key_Up);
1560     QCOMPARE(input->cursorPosition(),2);
1561     simulateKey(&canvas, Qt::Key_Down);
1562     QCOMPARE(input->cursorPosition(),2);
1563 }
1564
1565 void tst_qquicktextinput::navigation_RTL()
1566 {
1567     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1568     canvas.show();
1569     canvas.requestActivateWindow();
1570
1571     QVERIFY(canvas.rootObject() != 0);
1572
1573     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1574
1575     QVERIFY(input != 0);
1576     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1577     input->setText(QString::fromUtf16(arabic_str, 11));
1578
1579     input->setCursorPosition(0);
1580     QTRY_VERIFY(input->hasActiveFocus() == true);
1581
1582     // move off
1583     simulateKey(&canvas, Qt::Key_Right);
1584     QVERIFY(input->hasActiveFocus() == false);
1585
1586     // move back
1587     simulateKey(&canvas, Qt::Key_Left);
1588     QVERIFY(input->hasActiveFocus() == true);
1589
1590     input->setCursorPosition(input->text().length());
1591     QVERIFY(input->hasActiveFocus() == true);
1592
1593     // move off
1594     simulateKey(&canvas, Qt::Key_Left);
1595     QVERIFY(input->hasActiveFocus() == false);
1596
1597     // move back
1598     simulateKey(&canvas, Qt::Key_Right);
1599     QVERIFY(input->hasActiveFocus() == true);
1600 }
1601
1602 void tst_qquicktextinput::copyAndPaste() {
1603 #ifndef QT_NO_CLIPBOARD
1604
1605 #ifdef Q_WS_MAC
1606     {
1607         PasteboardRef pasteboard;
1608         OSStatus status = PasteboardCreate(0, &pasteboard);
1609         if (status == noErr)
1610             CFRelease(pasteboard);
1611         else
1612             QSKIP("This machine doesn't support the clipboard");
1613     }
1614 #endif
1615
1616     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1617     QDeclarativeComponent textInputComponent(&engine);
1618     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1619     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1620     QVERIFY(textInput != 0);
1621
1622     // copy and paste
1623     QCOMPARE(textInput->text().length(), 12);
1624     textInput->select(0, textInput->text().length());;
1625     textInput->copy();
1626     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1627     QCOMPARE(textInput->selectedText().length(), 12);
1628     textInput->setCursorPosition(0);
1629     QVERIFY(textInput->canPaste());
1630     textInput->paste();
1631     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1632     QCOMPARE(textInput->text().length(), 24);
1633
1634     // can paste
1635     QVERIFY(textInput->canPaste());
1636     textInput->setReadOnly(true);
1637     QVERIFY(!textInput->canPaste());
1638     textInput->setReadOnly(false);
1639     QVERIFY(textInput->canPaste());
1640
1641     // select word
1642     textInput->setCursorPosition(0);
1643     textInput->selectWord();
1644     QCOMPARE(textInput->selectedText(), QString("Hello"));
1645
1646     // select all and cut
1647     textInput->selectAll();
1648     textInput->cut();
1649     QCOMPARE(textInput->text().length(), 0);
1650     textInput->paste();
1651     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1652     QCOMPARE(textInput->text().length(), 24);
1653
1654     // clear copy buffer
1655     QClipboard *clipboard = QGuiApplication::clipboard();
1656     QVERIFY(clipboard);
1657     clipboard->clear();
1658     QVERIFY(!textInput->canPaste());
1659
1660     // test that copy functionality is disabled
1661     // when echo mode is set to hide text/password mode
1662     int index = 0;
1663     while (index < 4) {
1664         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1665         textInput->setEchoMode(echoMode);
1666         textInput->setText("My password");
1667         textInput->select(0, textInput->text().length());;
1668         textInput->copy();
1669         if (echoMode == QQuickTextInput::Normal) {
1670             QVERIFY(!clipboard->text().isEmpty());
1671             QCOMPARE(clipboard->text(), QString("My password"));
1672             clipboard->clear();
1673         } else {
1674             QVERIFY(clipboard->text().isEmpty());
1675         }
1676         index++;
1677     }
1678
1679     delete textInput;
1680 #endif
1681 }
1682
1683 void tst_qquicktextinput::canPasteEmpty() {
1684 #ifndef QT_NO_CLIPBOARD
1685
1686     QGuiApplication::clipboard()->clear();
1687
1688     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1689     QDeclarativeComponent textInputComponent(&engine);
1690     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1691     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1692     QVERIFY(textInput != 0);
1693
1694     QLineControl lc;
1695     bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1696     QCOMPARE(textInput->canPaste(), cp);
1697
1698 #endif
1699 }
1700
1701 void tst_qquicktextinput::canPaste() {
1702 #ifndef QT_NO_CLIPBOARD
1703
1704     QGuiApplication::clipboard()->setText("Some text");
1705
1706     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1707     QDeclarativeComponent textInputComponent(&engine);
1708     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1709     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1710     QVERIFY(textInput != 0);
1711
1712     QLineControl lc;
1713     bool cp = !lc.isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1714     QCOMPARE(textInput->canPaste(), cp);
1715
1716 #endif
1717 }
1718
1719 void tst_qquicktextinput::passwordCharacter()
1720 {
1721     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1722     QDeclarativeComponent textInputComponent(&engine);
1723     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1724     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1725     QVERIFY(textInput != 0);
1726
1727     textInput->setPasswordCharacter("X");
1728     qreal implicitWidth = textInput->implicitWidth();
1729     textInput->setPasswordCharacter(".");
1730
1731     // QTBUG-12383 content is updated and redrawn
1732     QVERIFY(textInput->implicitWidth() < implicitWidth);
1733
1734     delete textInput;
1735 }
1736
1737 void tst_qquicktextinput::cursorDelegate()
1738 {
1739     QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1740     view.show();
1741     view.requestActivateWindow();
1742     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1743     QVERIFY(textInputObject != 0);
1744     QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1745     //Test Delegate gets created
1746     textInputObject->setFocus(true);
1747     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1748     QVERIFY(delegateObject);
1749     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1750     //Test Delegate gets moved
1751     for (int i=0; i<= textInputObject->text().length(); i++) {
1752         textInputObject->setCursorPosition(i);
1753         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1754         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1755     }
1756     textInputObject->setCursorPosition(0);
1757     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1758     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1759     //Test Delegate gets deleted
1760     textInputObject->setCursorDelegate(0);
1761     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1762 }
1763
1764 void tst_qquicktextinput::cursorVisible()
1765 {
1766     QQuickView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1767     view.show();
1768     view.requestActivateWindow();
1769     QTest::qWaitForWindowShown(&view);
1770     QTRY_COMPARE(&view, qGuiApp->focusWindow());
1771
1772     QQuickTextInput input;
1773     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1774
1775     QCOMPARE(input.isCursorVisible(), false);
1776
1777     input.setCursorVisible(true);
1778     QCOMPARE(input.isCursorVisible(), true);
1779     QCOMPARE(spy.count(), 1);
1780
1781     input.setCursorVisible(false);
1782     QCOMPARE(input.isCursorVisible(), false);
1783     QCOMPARE(spy.count(), 2);
1784
1785     input.setFocus(true);
1786     QCOMPARE(input.isCursorVisible(), false);
1787     QCOMPARE(spy.count(), 2);
1788
1789     input.setParentItem(view.rootObject());
1790     QCOMPARE(input.isCursorVisible(), true);
1791     QCOMPARE(spy.count(), 3);
1792
1793     input.setFocus(false);
1794     QCOMPARE(input.isCursorVisible(), false);
1795     QCOMPARE(spy.count(), 4);
1796
1797     input.setFocus(true);
1798     QCOMPARE(input.isCursorVisible(), true);
1799     QCOMPARE(spy.count(), 5);
1800
1801     QQuickView alternateView;
1802     alternateView.show();
1803     alternateView.requestActivateWindow();
1804     QTest::qWaitForWindowShown(&alternateView);
1805
1806     QCOMPARE(input.isCursorVisible(), false);
1807     QCOMPARE(spy.count(), 6);
1808
1809     view.requestActivateWindow();
1810     QTest::qWaitForWindowShown(&view);
1811     QCOMPARE(input.isCursorVisible(), true);
1812     QCOMPARE(spy.count(), 7);
1813 }
1814
1815 void tst_qquicktextinput::cursorRectangle()
1816 {
1817     QSKIP("QTBUG-21689");
1818
1819     QString text = "Hello World!";
1820
1821     QQuickTextInput input;
1822     input.setText(text);
1823     QFontMetricsF fm(input.font());
1824     input.setWidth(fm.width(text.mid(0, 5)));
1825
1826     QRect r;
1827
1828     // some tolerance for different fonts.
1829 #ifdef Q_OS_LINUX
1830     const int error = 2;
1831 #else
1832     const int error = 5;
1833 #endif
1834
1835
1836     for (int i = 0; i <= 5; ++i) {
1837         input.setCursorPosition(i);
1838         r = input.cursorRectangle();
1839         int textWidth = fm.width(text.mid(0, i));
1840
1841         QVERIFY(r.left() < textWidth + error);
1842         QVERIFY(r.right() > textWidth - error);
1843         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1844     }
1845
1846     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1847     QVERIFY(r.left() < input.boundingRect().width());
1848     QVERIFY(r.right() >= input.width() - error);
1849
1850     for (int i = 6; i < text.length(); ++i) {
1851         input.setCursorPosition(i);
1852         QCOMPARE(r, input.cursorRectangle());
1853         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1854     }
1855
1856     for (int i = text.length() - 2; i >= 0; --i) {
1857         input.setCursorPosition(i);
1858         r = input.cursorRectangle();
1859         QVERIFY(r.right() >= 0);
1860         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1861     }
1862
1863     input.setText("Hi!");
1864     input.setHAlign(QQuickTextInput::AlignRight);
1865     r = input.cursorRectangle();
1866     QVERIFY(r.left() < input.boundingRect().width());
1867     QVERIFY(r.right() >= input.width() - error);
1868 }
1869
1870 void tst_qquicktextinput::readOnly()
1871 {
1872     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1873     canvas.show();
1874     canvas.requestActivateWindow();
1875
1876     QVERIFY(canvas.rootObject() != 0);
1877
1878     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1879
1880     QVERIFY(input != 0);
1881     QTRY_VERIFY(input->hasActiveFocus() == true);
1882     QVERIFY(input->isReadOnly() == true);
1883     QString initial = input->text();
1884     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1885         simulateKey(&canvas, k);
1886     simulateKey(&canvas, Qt::Key_Return);
1887     simulateKey(&canvas, Qt::Key_Space);
1888     simulateKey(&canvas, Qt::Key_Escape);
1889     QCOMPARE(input->text(), initial);
1890
1891     input->setCursorPosition(3);
1892     input->setReadOnly(false);
1893     QCOMPARE(input->isReadOnly(), false);
1894     QCOMPARE(input->cursorPosition(), input->text().length());
1895 }
1896
1897 void tst_qquicktextinput::echoMode()
1898 {
1899     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1900     canvas.show();
1901     canvas.requestActivateWindow();
1902     QTest::qWaitForWindowShown(&canvas);
1903     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1904
1905     QVERIFY(canvas.rootObject() != 0);
1906
1907     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1908
1909     QVERIFY(input != 0);
1910     QTRY_VERIFY(input->hasActiveFocus() == true);
1911     QString initial = input->text();
1912     Qt::InputMethodHints ref;
1913     QCOMPARE(initial, QLatin1String("ABCDefgh"));
1914     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
1915     QCOMPARE(input->displayText(), input->text());
1916     //Normal
1917     ref &= ~Qt::ImhHiddenText;
1918     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1919     QCOMPARE(input->inputMethodHints(), ref);
1920     input->setEchoMode(QQuickTextInput::NoEcho);
1921     QCOMPARE(input->text(), initial);
1922     QCOMPARE(input->displayText(), QLatin1String(""));
1923     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1924     //NoEcho
1925     ref |= Qt::ImhHiddenText;
1926     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1927     QCOMPARE(input->inputMethodHints(), ref);
1928     input->setEchoMode(QQuickTextInput::Password);
1929     //Password
1930     ref |= Qt::ImhHiddenText;
1931     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1932     QCOMPARE(input->text(), initial);
1933     QCOMPARE(input->displayText(), QLatin1String("********"));
1934     QCOMPARE(input->inputMethodHints(), ref);
1935     input->setPasswordCharacter(QChar('Q'));
1936     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
1937     QCOMPARE(input->text(), initial);
1938     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1939     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
1940     //PasswordEchoOnEdit
1941     ref &= ~Qt::ImhHiddenText;
1942     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1943     QCOMPARE(input->inputMethodHints(), ref);
1944     QCOMPARE(input->text(), initial);
1945     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
1946     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
1947     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
1948     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1949     QCOMPARE(input->text(), QLatin1String("a"));
1950     QCOMPARE(input->displayText(), QLatin1String("a"));
1951     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
1952     input->setFocus(false);
1953     QVERIFY(input->hasActiveFocus() == false);
1954     QCOMPARE(input->displayText(), QLatin1String("Q"));
1955     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
1956     input->setFocus(true);
1957     QVERIFY(input->hasActiveFocus());
1958     QInputMethodEvent inputEvent;
1959     inputEvent.setCommitString(initial);
1960     QGuiApplication::sendEvent(input, &inputEvent);
1961     QCOMPARE(input->text(), initial);
1962     QCOMPARE(input->displayText(), initial);
1963     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
1964 }
1965
1966 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
1967 void tst_qdeclarativetextinput::passwordEchoDelay()
1968 {
1969     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("echoMode.qml")));
1970     canvas.show();
1971     canvas.setFocus();
1972     QGuiApplication::setActiveWindow(&canvas);
1973     QTest::qWaitForWindowShown(&canvas);
1974     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1975
1976     QVERIFY(canvas.rootObject() != 0);
1977
1978     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1979
1980     QChar fillChar = QLatin1Char('*');
1981
1982     input->setEchoMode(QDeclarativeTextInput::Password);
1983     QCOMPARE(input->displayText(), QString(8, fillChar));
1984     input->setText(QString());
1985     QCOMPARE(input->displayText(), QString());
1986
1987     QTest::keyPress(&canvas, '0');
1988     QTest::keyPress(&canvas, '1');
1989     QTest::keyPress(&canvas, '2');
1990     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
1991     QTest::keyPress(&canvas, '3');
1992     QTest::keyPress(&canvas, '4');
1993     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1994     QTest::keyPress(&canvas, Qt::Key_Backspace);
1995     QCOMPARE(input->displayText(), QString(4, fillChar));
1996     QTest::keyPress(&canvas, '4');
1997     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
1998     QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
1999     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2000     QTest::keyPress(&canvas, '5');
2001     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2002     input->setFocus(false);
2003     QVERIFY(!input->hasFocus());
2004     QCOMPARE(input->displayText(), QString(6, fillChar));
2005     input->setFocus(true);
2006     QTRY_VERIFY(input->hasFocus());
2007     QCOMPARE(input->displayText(), QString(6, fillChar));
2008     QTest::keyPress(&canvas, '6');
2009     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2010
2011     QInputMethodEvent ev;
2012     ev.setCommitString(QLatin1String("7"));
2013     QGuiApplication::sendEvent(&canvas, &ev);
2014     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2015 }
2016 #endif
2017
2018
2019 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2020 {
2021     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2022     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2023
2024     QGuiApplication::sendEvent(view, &press);
2025     QGuiApplication::sendEvent(view, &release);
2026 }
2027
2028 #ifndef QTBUG_21691
2029 class MyInputContext : public QInputContext
2030 {
2031 public:
2032     MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
2033     ~MyInputContext() {}
2034
2035     QString identifierName() { return QString(); }
2036     QString language() { return QString(); }
2037
2038     void reset() {}
2039
2040     bool isComposing() const { return false; }
2041
2042     void update() { updateReceived = true; }
2043
2044     void mouseHandler(int x, QMouseEvent *event)
2045     {
2046         cursor = x;
2047         eventType = event->type();
2048         eventPosition = event->pos();
2049         eventGlobalPosition = event->globalPos();
2050         eventButton = event->button();
2051         eventButtons = event->buttons();
2052         eventModifiers = event->modifiers();
2053     }
2054
2055     void sendPreeditText(const QString &text, int cursor)
2056     {
2057         QList<QInputMethodEvent::Attribute> attributes;
2058         attributes.append(QInputMethodEvent::Attribute(
2059                 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2060
2061         QInputMethodEvent event(text, attributes);
2062         sendEvent(event);
2063     }
2064
2065     bool updateReceived;
2066     int cursor;
2067     QEvent::Type eventType;
2068     QPoint eventPosition;
2069     QPoint eventGlobalPosition;
2070     Qt::MouseButton eventButton;
2071     Qt::MouseButtons eventButtons;
2072     Qt::KeyboardModifiers eventModifiers;
2073 };
2074 #endif
2075
2076 void tst_qquicktextinput::openInputPanel()
2077 {
2078     QQuickView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2079     view.show();
2080     view.requestActivateWindow();
2081     QTest::qWaitForWindowShown(&view);
2082     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2083
2084     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2085     QVERIFY(input);
2086
2087     // check default values
2088     QVERIFY(input->focusOnPress());
2089     QVERIFY(!input->hasActiveFocus());
2090     qDebug() << &input << qApp->inputPanel()->inputItem();
2091     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2092     QEXPECT_FAIL("", "QTBUG-21946", Abort);
2093     QCOMPARE(qApp->inputPanel()->visible(), false);
2094
2095     // input panel should open on focus
2096     QPoint centerPoint(view.width()/2, view.height()/2);
2097     Qt::KeyboardModifiers noModifiers = 0;
2098     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2099     QGuiApplication::processEvents();
2100     QVERIFY(input->hasActiveFocus());
2101     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2102     QCOMPARE(qApp->inputPanel()->visible(), true);
2103     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2104
2105     // input panel should be re-opened when pressing already focused TextInput
2106     qApp->inputPanel()->hide();
2107     QCOMPARE(qApp->inputPanel()->visible(), false);
2108     QVERIFY(input->hasActiveFocus());
2109     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2110     QGuiApplication::processEvents();
2111     QCOMPARE(qApp->inputPanel()->visible(), true);
2112     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2113
2114     // input panel should stay visible if focus is lost to another text inputor
2115     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2116     QQuickTextInput anotherInput;
2117     anotherInput.setParentItem(view.rootObject());
2118     anotherInput.setFocus(true);
2119     QCOMPARE(qApp->inputPanel()->visible(), true);
2120     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2121     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2122
2123     anotherInput.setFocus(false);
2124     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2125     QCOMPARE(view.activeFocusItem(), view.rootItem());
2126     anotherInput.setFocus(true);
2127
2128     // input item should be null if focus is lost to an item that doesn't accept inputs
2129     QQuickItem item;
2130     item.setParentItem(view.rootObject());
2131     item.setFocus(true);
2132     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2133     QCOMPARE(view.activeFocusItem(), &item);
2134
2135     qApp->inputPanel()->hide();
2136
2137     // input panel should not be opened if TextInput is read only
2138     input->setReadOnly(true);
2139     input->setFocus(true);
2140     QCOMPARE(qApp->inputPanel()->visible(), false);
2141     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2142     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2143     QGuiApplication::processEvents();
2144     QCOMPARE(qApp->inputPanel()->visible(), false);
2145
2146     // input panel should not be opened if focusOnPress is set to false
2147     input->setFocusOnPress(false);
2148     input->setFocus(false);
2149     input->setFocus(true);
2150     QCOMPARE(qApp->inputPanel()->visible(), false);
2151     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2152     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2153     QCOMPARE(qApp->inputPanel()->visible(), false);
2154
2155     // input panel should open when openSoftwareInputPanel is called
2156     input->openSoftwareInputPanel();
2157     QCOMPARE(qApp->inputPanel()->visible(), true);
2158
2159     // input panel should close when closeSoftwareInputPanel is called
2160     input->closeSoftwareInputPanel();
2161     QCOMPARE(qApp->inputPanel()->visible(), false);
2162 }
2163
2164 class MyTextInput : public QQuickTextInput
2165 {
2166 public:
2167     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2168     {
2169         nbPaint = 0;
2170     }
2171     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2172     {
2173        nbPaint++;
2174        return QQuickTextInput::updatePaintNode(node, data);
2175     }
2176     int nbPaint;
2177 };
2178
2179 void tst_qquicktextinput::setHAlignClearCache()
2180 {
2181     QQuickView view;
2182     MyTextInput input;
2183     input.setText("Hello world");
2184     input.setParentItem(view.rootItem());
2185     view.show();
2186     view.requestActivateWindow();
2187     QTest::qWaitForWindowShown(&view);
2188     QTRY_COMPARE(input.nbPaint, 1);
2189     input.setHAlign(QQuickTextInput::AlignRight);
2190     //Changing the alignment should trigger a repaint
2191     QTRY_COMPARE(input.nbPaint, 2);
2192 }
2193
2194 void tst_qquicktextinput::focusOutClearSelection()
2195 {
2196     QQuickView view;
2197     QQuickTextInput input;
2198     QQuickTextInput input2;
2199     input.setText(QLatin1String("Hello world"));
2200     input.setFocus(true);
2201     input2.setParentItem(view.rootItem());
2202     input.setParentItem(view.rootItem());
2203     view.show();
2204     view.requestActivateWindow();
2205     QTest::qWaitForWindowShown(&view);
2206     input.select(2,5);
2207     //The selection should work
2208     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2209     input2.setFocus(true);
2210     QGuiApplication::processEvents();
2211     //The input lost the focus selection should be cleared
2212     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2213 }
2214
2215 void tst_qquicktextinput::geometrySignals()
2216 {
2217     QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2218     QObject *o = component.create();
2219     QVERIFY(o);
2220     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2221     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2222     delete o;
2223 }
2224
2225 void tst_qquicktextinput::testQtQuick11Attributes()
2226 {
2227     QFETCH(QString, code);
2228     QFETCH(QString, warning);
2229     QFETCH(QString, error);
2230
2231     QDeclarativeEngine engine;
2232     QObject *obj;
2233
2234     QDeclarativeComponent valid(&engine);
2235     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2236     obj = valid.create();
2237     QVERIFY(obj);
2238     QVERIFY(valid.errorString().isEmpty());
2239     delete obj;
2240
2241     QDeclarativeComponent invalid(&engine);
2242     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2243     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2244     obj = invalid.create();
2245     QCOMPARE(invalid.errorString(), error);
2246     delete obj;
2247 }
2248
2249 void tst_qquicktextinput::testQtQuick11Attributes_data()
2250 {
2251     QTest::addColumn<QString>("code");
2252     QTest::addColumn<QString>("warning");
2253     QTest::addColumn<QString>("error");
2254
2255     QTest::newRow("canPaste") << "property bool foo: canPaste"
2256         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2257         << "";
2258
2259     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2260         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2261         << "";
2262
2263     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2264         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2265         << "";
2266 }
2267
2268 void tst_qquicktextinput::preeditAutoScroll()
2269 {
2270 #ifdef QTBUG_21691
2271     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2272     QVERIFY(false);
2273 #else
2274     QString preeditText = "califragisiticexpialidocious!";
2275
2276     QQuickView view(QUrl::fromLocalFile(TESTDATA("preeditAutoScroll.qml")));
2277     MyInputContext ic;
2278     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2279     // and QWidget won't allow an input context to be set when the flag is not set.
2280     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2281     view.setInputContext(&ic);
2282     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2283     view.show();
2284     view.requestActivateWindow();
2285     QTest::qWaitForWindowShown(&view);
2286     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2287     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2288     QVERIFY(input);
2289
2290     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2291     int cursorRectangleChanges = 0;
2292
2293     QFontMetricsF fm(input->font());
2294     input->setWidth(fm.width(input->text()));
2295
2296     // test the text is scrolled so the preedit is visible.
2297     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2298     QVERIFY(input->positionAt(0) != 0);
2299     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2300     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2301
2302     // test the text is scrolled back when the preedit is removed.
2303     ic.sendEvent(QInputMethodEvent());
2304     QCOMPARE(input->positionAt(0), 0);
2305     QCOMPARE(input->positionAt(input->width()), 5);
2306     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2307
2308     // some tolerance for different fonts.
2309 #ifdef Q_OS_LINUX
2310     const int error = 2;
2311 #else
2312     const int error = 5;
2313 #endif
2314
2315     // test if the preedit is larger than the text input that the
2316     // character preceding the cursor is still visible.
2317     qreal x = input->positionToRectangle(0).x();
2318     for (int i = 0; i < 3; ++i) {
2319         ic.sendPreeditText(preeditText, i + 1);
2320         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2321         QVERIFY(input->positionToRectangle(0).x() < x);
2322         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2323         x = input->positionToRectangle(0).x();
2324     }
2325     for (int i = 1; i >= 0; --i) {
2326         ic.sendPreeditText(preeditText, i + 1);
2327         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2328         QVERIFY(input->positionToRectangle(0).x() > x);
2329         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2330         x = input->positionToRectangle(0).x();
2331     }
2332
2333     // Test incrementing the preedit cursor doesn't cause further
2334     // scrolling when right most text is visible.
2335     ic.sendPreeditText(preeditText, preeditText.length() - 3);
2336     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2337     x = input->positionToRectangle(0).x();
2338     for (int i = 2; i >= 0; --i) {
2339         ic.sendPreeditText(preeditText, preeditText.length() - i);
2340         QCOMPARE(input->positionToRectangle(0).x(), x);
2341         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2342     }
2343     for (int i = 1; i <  3; ++i) {
2344         ic.sendPreeditText(preeditText, preeditText.length() - i);
2345         QCOMPARE(input->positionToRectangle(0).x(), x);
2346         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2347     }
2348
2349     // Test disabling auto scroll.
2350     ic.sendEvent(QInputMethodEvent());
2351
2352     input->setAutoScroll(false);
2353     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2354     QCOMPARE(input->positionAt(0), 0);
2355     QCOMPARE(input->positionAt(input->width()), 5);
2356 #endif
2357 }
2358
2359 void tst_qquicktextinput::preeditMicroFocus()
2360 {
2361 #ifdef QTBUG_21691
2362     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2363     QVERIFY(false);
2364 #else
2365     QString preeditText = "super";
2366
2367     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2368     MyInputContext ic;
2369     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2370     // and QWidget won't allow an input context to be set when the flag is not set.
2371     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2372     view.setInputContext(&ic);
2373     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2374     view.show();
2375     view.requestActivateWindow();
2376     QTest::qWaitForWindowShown(&view);
2377     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2378     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2379     QVERIFY(input);
2380
2381     QRect currentRect;
2382     QRect previousRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2383
2384     // Verify that the micro focus rect is positioned the same for position 0 as
2385     // it would be if there was no preedit text.
2386     ic.updateReceived = false;
2387     ic.sendPreeditText(preeditText, 0);
2388     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2389     QCOMPARE(currentRect, previousRect);
2390 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2391     QCOMPARE(ic.updateReceived, true);
2392 #endif
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 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2402         QCOMPARE(ic.updateReceived, true);
2403 #endif
2404         previousRect = currentRect;
2405     }
2406
2407     // Verify that if there is no preedit cursor then the micro focus rect is the
2408     // same as it would be if it were positioned at the end of the preedit text.
2409     ic.sendPreeditText(preeditText, 0);
2410     ic.updateReceived = false;
2411     ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2412     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2413     QCOMPARE(currentRect, previousRect);
2414 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2415     QCOMPARE(ic.updateReceived, true);
2416 #endif
2417 #endif
2418 }
2419
2420 void tst_qquicktextinput::inputContextMouseHandler()
2421 {
2422 #ifdef QTBUG_21691
2423     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2424     QVERIFY(false);
2425 #else
2426     QString text = "supercalifragisiticexpialidocious!";
2427
2428     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2429     MyInputContext ic;
2430     // QQuickCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2431     // and QWidget won't allow an input context to be set when the flag is not set.
2432     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2433     view.setInputContext(&ic);
2434     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2435     view.show();
2436     view.requestActivateWindow();
2437     QTest::qWaitForWindowShown(&view);
2438     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2439     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2440     QVERIFY(input);
2441
2442     QFontMetricsF fm(input->font());
2443     const qreal y = fm.height() / 2;
2444
2445     QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2446     QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2447     QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2448     QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2449     QPoint globalPosition2 = view.mapToGlobal(position2);
2450     QPoint globalposition8 = view.mapToGlobal(position8);
2451     QPoint globalposition20 = view.mapToGlobal(position20);
2452     QPoint globalposition27 = view.mapToGlobal(position27);
2453
2454     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2455
2456     QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2457     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2458     QCOMPARE(ic.eventPosition, position2);
2459     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2460     QCOMPARE(ic.eventButton, Qt::LeftButton);
2461     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2462     QVERIFY(ic.cursor < 0);
2463     ic.eventType = QEvent::None;
2464
2465     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2466     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2467     QCOMPARE(ic.eventPosition, position2);
2468     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2469     QCOMPARE(ic.eventButton, Qt::LeftButton);
2470     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2471     QVERIFY(ic.cursor < 0);
2472     ic.eventType = QEvent::None;
2473
2474     {   QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2475         QGuiApplication::sendEvent(&view, &mv); }
2476     QCOMPARE(ic.eventType, QEvent::None);
2477
2478     {   QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2479         QGuiApplication::sendEvent(&view, &mv); }
2480     QCOMPARE(ic.eventType, QEvent::MouseMove);
2481     QCOMPARE(ic.eventPosition, position27);
2482     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2483     QCOMPARE(ic.eventButton, Qt::LeftButton);
2484     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2485     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);    // 15 is expected but some platforms may be off by one.
2486     ic.eventType = QEvent::None;
2487
2488     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2489     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2490     QCOMPARE(ic.eventPosition, position27);
2491     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2492     QCOMPARE(ic.eventButton, Qt::LeftButton);
2493     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2494     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2495     ic.eventType = QEvent::None;
2496
2497     // And in the other direction.
2498     QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2499     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2500     QCOMPARE(ic.eventPosition, position27);
2501     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2502     QCOMPARE(ic.eventButton, Qt::LeftButton);
2503     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2504     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2505     ic.eventType = QEvent::None;
2506
2507     QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2508     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2509     QCOMPARE(ic.eventPosition, position27);
2510     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2511     QCOMPARE(ic.eventButton, Qt::RightButton);
2512     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2513     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2514     ic.eventType = QEvent::None;
2515
2516     {   QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2517         QGuiApplication::sendEvent(&view, &mv); }
2518     QCOMPARE(ic.eventType, QEvent::MouseMove);
2519     QCOMPARE(ic.eventPosition, position20);
2520     QCOMPARE(ic.eventGlobalPosition, globalposition20);
2521     QCOMPARE(ic.eventButton, Qt::RightButton);
2522     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2523     QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2524     ic.eventType = QEvent::None;
2525
2526     {   QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2527         QGuiApplication::sendEvent(&view, &mv); }
2528     QCOMPARE(ic.eventType, QEvent::None);
2529
2530     QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2531     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2532     QCOMPARE(ic.eventPosition, position2);
2533     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2534     QCOMPARE(ic.eventButton, Qt::RightButton);
2535     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2536     QVERIFY(ic.cursor < 0);
2537     ic.eventType = QEvent::None;
2538 #endif
2539 }
2540
2541 void tst_qquicktextinput::inputMethodComposing()
2542 {
2543     QString text = "supercalifragisiticexpialidocious!";
2544
2545     QQuickView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2546     view.show();
2547     view.requestActivateWindow();
2548     QTest::qWaitForWindowShown(&view);
2549     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2550     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2551     QVERIFY(input);
2552     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2553
2554     QCOMPARE(input->isInputMethodComposing(), false);
2555     {
2556         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2557         QGuiApplication::sendEvent(input, &event);
2558     }
2559     QCOMPARE(input->isInputMethodComposing(), true);
2560     QCOMPARE(spy.count(), 1);
2561
2562     {
2563         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2564         QGuiApplication::sendEvent(input, &event);
2565     }
2566     QCOMPARE(spy.count(), 1);
2567
2568     {
2569         QInputMethodEvent event;
2570         QGuiApplication::sendEvent(input, &event);
2571     }
2572     QCOMPARE(input->isInputMethodComposing(), false);
2573     QCOMPARE(spy.count(), 2);
2574 }
2575
2576 void tst_qquicktextinput::cursorRectangleSize()
2577 {
2578     QQuickView *canvas = new QQuickView(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
2579     QVERIFY(canvas->rootObject() != 0);
2580     canvas->show();
2581     canvas->requestActivateWindow();
2582     QTest::qWaitForWindowShown(canvas);
2583
2584     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2585     QVERIFY(textInput != 0);
2586     textInput->setFocus(Qt::OtherFocusReason);
2587     QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2588     QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2589     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2590     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2591
2592     QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2593
2594     QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2595     QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2596
2597     delete canvas;
2598 }
2599
2600 void tst_qquicktextinput::tripleClickSelectsAll()
2601 {
2602     QString qmlfile = TESTDATA("positionAt.qml");
2603     QQuickView view(QUrl::fromLocalFile(qmlfile));
2604     view.show();
2605     view.requestActivateWindow();
2606     QTest::qWaitForWindowShown(&view);
2607
2608     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2609
2610     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2611     QVERIFY(input);
2612
2613     QLatin1String hello("Hello world!");
2614     input->setSelectByMouse(true);
2615     input->setText(hello);
2616
2617     // Clicking on the same point inside TextInput three times in a row
2618     // should trigger a triple click, thus selecting all the text.
2619     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2620     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2621     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2622     QGuiApplication::processEvents();
2623     QCOMPARE(input->selectedText(), hello);
2624
2625     // Now it simulates user moving the mouse between the second and the third click.
2626     // In this situation, we don't expect a triple click.
2627     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2628     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2629     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2630     QGuiApplication::processEvents();
2631     QVERIFY(input->selectedText().isEmpty());
2632
2633     // And now we press the third click too late, so no triple click event is triggered.
2634     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2635     QGuiApplication::processEvents();
2636     QTest::qWait(QApplication::doubleClickInterval() + 1);
2637     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2638     QGuiApplication::processEvents();
2639     QVERIFY(input->selectedText().isEmpty());
2640 }
2641
2642 void tst_qquicktextinput::QTBUG_19956_data()
2643 {
2644     QTest::addColumn<QString>("url");
2645     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2646     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2647 }
2648
2649 void tst_qquicktextinput::QTBUG_19956()
2650 {
2651     QFETCH(QString, url);
2652
2653     QQuickView canvas(QUrl::fromLocalFile(TESTDATA(url)));
2654     canvas.show();
2655     canvas.requestActivateWindow();
2656     QTest::qWaitForWindowShown(&canvas);
2657     QVERIFY(canvas.rootObject() != 0);
2658     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2659     QVERIFY(input);
2660     input->setFocus(true);
2661     QVERIFY(input->hasActiveFocus());
2662
2663     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
2664     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2665     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
2666     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2667
2668     canvas.rootObject()->setProperty("topvalue", 15);
2669     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
2670     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2671
2672     canvas.rootObject()->setProperty("topvalue", 25);
2673     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
2674     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2675
2676     canvas.rootObject()->setProperty("bottomvalue", 21);
2677     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
2678     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2679
2680     canvas.rootObject()->setProperty("bottomvalue", 10);
2681     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
2682     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2683 }
2684
2685 void tst_qquicktextinput::QTBUG_19956_regexp()
2686 {
2687     QQuickView canvas(QUrl::fromLocalFile(TESTDATA("qtbug-19956regexp.qml")));
2688     canvas.show();
2689     canvas.requestActivateWindow();
2690     QTest::qWaitForWindowShown(&canvas);
2691     QVERIFY(canvas.rootObject() != 0);
2692     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
2693     QVERIFY(input);
2694     input->setFocus(true);
2695     QVERIFY(input->hasActiveFocus());
2696
2697     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2698     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2699     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
2700     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2701
2702     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
2703     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
2704     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
2705
2706     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
2707     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
2708     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
2709 }
2710
2711 QTEST_MAIN(tst_qquicktextinput)
2712
2713 #include "tst_qquicktextinput.moc"