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