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