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