5f9771a2895833818fa811c0c5e5d1c0090902a2
[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 <QInputPanel>
49 #include <private/qsgtextinput_p.h>
50 #include <private/qsgtextinput_p_p.h>
51 #include <QDebug>
52 #include <QDir>
53 #include <QStyle>
54 #include <QInputContext>
55 #include <private/qsgdistancefieldglyphcache_p.h>
56 #include <QtOpenGL/QGLShaderProgram>
57 #include <math.h>
58
59 #include "qplatformdefs.h"
60
61 #ifdef Q_OS_SYMBIAN
62 // In Symbian OS test data is located in applications private dir
63 #define SRCDIR "."
64 #endif
65
66 Q_DECLARE_METATYPE(QSGTextInput::SelectionMode)
67 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
68
69 #define QTBUG_21691
70 #define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext"
71
72 #define QTBUG_21489_MESSAGE "Pre-condition failure because of QTBUG-21489. This can be safely ignored if there no subsequent failures"
73
74 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
75 {
76     // XXX This will be replaced by some clever persistent platform image store.
77     QString persistent_dir = SRCDIR "/data";
78     QString arch = "unknown-architecture"; // QTest needs to help with this.
79
80     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
81
82     if (!QFile::exists(expectfile)) {
83         actual.save(expectfile);
84         qWarning() << "created" << expectfile;
85     }
86
87     return expectfile;
88 }
89
90 class tst_qsgtextinput : public QObject
91
92 {
93     Q_OBJECT
94 public:
95     tst_qsgtextinput();
96
97 private slots:
98     void initTestCase();
99     void cleanupTestCase();
100     void text();
101     void width();
102     void font();
103     void color();
104     void selection();
105     void isRightToLeft_data();
106     void isRightToLeft();
107     void moveCursorSelection_data();
108     void moveCursorSelection();
109     void moveCursorSelectionSequence_data();
110     void moveCursorSelectionSequence();
111     void dragMouseSelection();
112     void mouseSelectionMode_data();
113     void mouseSelectionMode();
114     void tripleClickSelectsAll();
115
116     void horizontalAlignment_data();
117     void horizontalAlignment();
118     void horizontalAlignment_RightToLeft();
119
120     void positionAt();
121
122     void maxLength();
123     void masks();
124     void validators();
125     void inputMethods();
126
127     void passwordCharacter();
128     void cursorDelegate();
129     void cursorVisible();
130     void cursorRectangle();
131     void navigation();
132     void navigation_RTL();
133     void copyAndPaste();
134     void canPasteEmpty();
135     void canPaste();
136     void readOnly();
137
138     void openInputPanel();
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::ImCursorRectangle).toRect(), r);
1846     }
1847
1848     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1849     QVERIFY(r.left() < input.boundingRect().width());
1850     QVERIFY(r.right() >= input.width() - error);
1851
1852     for (int i = 6; i < text.length(); ++i) {
1853         input.setCursorPosition(i);
1854         QCOMPARE(r, input.cursorRectangle());
1855         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1856     }
1857
1858     for (int i = text.length() - 2; i >= 0; --i) {
1859         input.setCursorPosition(i);
1860         r = input.cursorRectangle();
1861         QVERIFY(r.right() >= 0);
1862         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
1863     }
1864
1865     input.setText("Hi!");
1866     input.setHAlign(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() : 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     void update() { updateReceived = true; }
2047
2048     void mouseHandler(int x, QMouseEvent *event)
2049     {
2050         cursor = x;
2051         eventType = event->type();
2052         eventPosition = event->pos();
2053         eventGlobalPosition = event->globalPos();
2054         eventButton = event->button();
2055         eventButtons = event->buttons();
2056         eventModifiers = event->modifiers();
2057     }
2058
2059     void sendPreeditText(const QString &text, int cursor)
2060     {
2061         QList<QInputMethodEvent::Attribute> attributes;
2062         attributes.append(QInputMethodEvent::Attribute(
2063                 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2064
2065         QInputMethodEvent event(text, attributes);
2066         sendEvent(event);
2067     }
2068
2069     bool updateReceived;
2070     int cursor;
2071     QEvent::Type eventType;
2072     QPoint eventPosition;
2073     QPoint eventGlobalPosition;
2074     Qt::MouseButton eventButton;
2075     Qt::MouseButtons eventButtons;
2076     Qt::KeyboardModifiers eventModifiers;
2077 };
2078 #endif
2079
2080 void tst_qsgtextinput::openInputPanel()
2081 {
2082     QSGView view(QUrl::fromLocalFile(SRCDIR "/data/openInputPanel.qml"));
2083     view.show();
2084     view.requestActivateWindow();
2085     QTest::qWaitForWindowShown(&view);
2086     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2087     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2088
2089     QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2090     QVERIFY(input);
2091
2092     // check default values
2093     QVERIFY(input->focusOnPress());
2094     QVERIFY(!input->hasActiveFocus());
2095     qDebug() << &input << qApp->inputPanel()->inputItem();
2096     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2097     QCOMPARE(qApp->inputPanel()->visible(), false);
2098
2099     // input panel should open on focus
2100     QPoint centerPoint(view.width()/2, view.height()/2);
2101     Qt::KeyboardModifiers noModifiers = 0;
2102     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2103     QGuiApplication::processEvents();
2104     QVERIFY(input->hasActiveFocus());
2105     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2106     QCOMPARE(qApp->inputPanel()->visible(), true);
2107     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2108
2109     // input panel should be re-opened when pressing already focused TextInput
2110     qApp->inputPanel()->hide();
2111     QCOMPARE(qApp->inputPanel()->visible(), false);
2112     QVERIFY(input->hasActiveFocus());
2113     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2114     QGuiApplication::processEvents();
2115     QCOMPARE(qApp->inputPanel()->visible(), true);
2116     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2117
2118     // input panel should stay visible if focus is lost to another text inputor
2119     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2120     QSGTextInput anotherInput;
2121     anotherInput.setParentItem(view.rootObject());
2122     anotherInput.setFocus(true);
2123     QCOMPARE(qApp->inputPanel()->visible(), true);
2124     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2125     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2126
2127     anotherInput.setFocus(false);
2128     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2129     QCOMPARE(view.activeFocusItem(), view.rootItem());
2130     anotherInput.setFocus(true);
2131
2132     // input item should be null if focus is lost to an item that doesn't accept inputs
2133     QSGItem item;
2134     item.setParentItem(view.rootObject());
2135     item.setFocus(true);
2136     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2137     QCOMPARE(view.activeFocusItem(), &item);
2138
2139     qApp->inputPanel()->hide();
2140
2141     // input panel should not be opened if TextInput is read only
2142     input->setReadOnly(true);
2143     input->setFocus(true);
2144     QCOMPARE(qApp->inputPanel()->visible(), false);
2145     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2146     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2147     QGuiApplication::processEvents();
2148     QCOMPARE(qApp->inputPanel()->visible(), false);
2149
2150     // input panel should not be opened if focusOnPress is set to false
2151     input->setFocusOnPress(false);
2152     input->setFocus(false);
2153     input->setFocus(true);
2154     QCOMPARE(qApp->inputPanel()->visible(), false);
2155     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2156     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2157     QCOMPARE(qApp->inputPanel()->visible(), false);
2158
2159     // input panel should open when openSoftwareInputPanel is called
2160     input->openSoftwareInputPanel();
2161     QCOMPARE(qApp->inputPanel()->visible(), true);
2162
2163     // input panel should close when closeSoftwareInputPanel is called
2164     input->closeSoftwareInputPanel();
2165     QCOMPARE(qApp->inputPanel()->visible(), false);
2166 }
2167
2168 class MyTextInput : public QSGTextInput
2169 {
2170 public:
2171     MyTextInput(QSGItem *parent = 0) : QSGTextInput(parent)
2172     {
2173         nbPaint = 0;
2174     }
2175     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2176     {
2177        nbPaint++;
2178        return QSGTextInput::updatePaintNode(node, data);
2179     }
2180     int nbPaint;
2181 };
2182
2183 void tst_qsgtextinput::setHAlignClearCache()
2184 {
2185     QSGView view;
2186     MyTextInput input;
2187     input.setText("Hello world");
2188     input.setParentItem(view.rootItem());
2189     view.show();
2190     view.requestActivateWindow();
2191     QTest::qWaitForWindowShown(&view);
2192     QTRY_COMPARE(input.nbPaint, 1);
2193     input.setHAlign(QSGTextInput::AlignRight);
2194     //Changing the alignment should trigger a repaint
2195     QTRY_COMPARE(input.nbPaint, 2);
2196 }
2197
2198 void tst_qsgtextinput::focusOutClearSelection()
2199 {
2200     QSGView view;
2201     QSGTextInput input;
2202     QSGTextInput input2;
2203     input.setText(QLatin1String("Hello world"));
2204     input.setFocus(true);
2205     input2.setParentItem(view.rootItem());
2206     input.setParentItem(view.rootItem());
2207     view.show();
2208     view.requestActivateWindow();
2209     QTest::qWaitForWindowShown(&view);
2210     input.select(2,5);
2211     //The selection should work
2212     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2213     input2.setFocus(true);
2214     QGuiApplication::processEvents();
2215     //The input lost the focus selection should be cleared
2216     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2217 }
2218
2219 void tst_qsgtextinput::geometrySignals()
2220 {
2221     QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
2222     QObject *o = component.create();
2223     QVERIFY(o);
2224     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2225     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2226     delete o;
2227 }
2228
2229 void tst_qsgtextinput::testQtQuick11Attributes()
2230 {
2231     QFETCH(QString, code);
2232     QFETCH(QString, warning);
2233     QFETCH(QString, error);
2234
2235     QDeclarativeEngine engine;
2236     QObject *obj;
2237
2238     QDeclarativeComponent valid(&engine);
2239     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2240     obj = valid.create();
2241     QVERIFY(obj);
2242     QVERIFY(valid.errorString().isEmpty());
2243     delete obj;
2244
2245     QDeclarativeComponent invalid(&engine);
2246     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2247     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2248     obj = invalid.create();
2249     QCOMPARE(invalid.errorString(), error);
2250     delete obj;
2251 }
2252
2253 void tst_qsgtextinput::testQtQuick11Attributes_data()
2254 {
2255     QTest::addColumn<QString>("code");
2256     QTest::addColumn<QString>("warning");
2257     QTest::addColumn<QString>("error");
2258
2259     QTest::newRow("canPaste") << "property bool foo: canPaste"
2260         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2261         << "";
2262
2263     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2264         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2265         << "";
2266
2267     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2268         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2269         << "";
2270 }
2271
2272 void tst_qsgtextinput::preeditAutoScroll()
2273 {
2274 #ifdef QTBUG_21691
2275     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2276     QVERIFY(false);
2277 #else
2278     QString preeditText = "califragisiticexpialidocious!";
2279
2280     QSGView view(QUrl::fromLocalFile(SRCDIR "/data/preeditAutoScroll.qml"));
2281     MyInputContext ic;
2282     // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2283     // and QWidget won't allow an input context to be set when the flag is not set.
2284     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2285     view.setInputContext(&ic);
2286     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2287     view.show();
2288     view.requestActivateWindow();
2289     QTest::qWaitForWindowShown(&view);
2290     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2291     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2292     QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2293     QVERIFY(input);
2294
2295     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2296     int cursorRectangleChanges = 0;
2297
2298     QFontMetricsF fm(input->font());
2299     input->setWidth(fm.width(input->text()));
2300
2301     // test the text is scrolled so the preedit is visible.
2302     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2303     QVERIFY(input->positionAt(0) != 0);
2304     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2305     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2306
2307     // test the text is scrolled back when the preedit is removed.
2308     ic.sendEvent(QInputMethodEvent());
2309     QCOMPARE(input->positionAt(0), 0);
2310     QCOMPARE(input->positionAt(input->width()), 5);
2311     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2312
2313     // some tolerance for different fonts.
2314 #ifdef Q_OS_LINUX
2315     const int error = 2;
2316 #else
2317     const int error = 5;
2318 #endif
2319
2320     // test if the preedit is larger than the text input that the
2321     // character preceding the cursor is still visible.
2322     qreal x = input->positionToRectangle(0).x();
2323     for (int i = 0; i < 3; ++i) {
2324         ic.sendPreeditText(preeditText, i + 1);
2325         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2326         QVERIFY(input->positionToRectangle(0).x() < x);
2327         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2328         x = input->positionToRectangle(0).x();
2329     }
2330     for (int i = 1; i >= 0; --i) {
2331         ic.sendPreeditText(preeditText, i + 1);
2332         QVERIFY(input->cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2333         QVERIFY(input->positionToRectangle(0).x() > x);
2334         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2335         x = input->positionToRectangle(0).x();
2336     }
2337
2338     // Test incrementing the preedit cursor doesn't cause further
2339     // scrolling when right most text is visible.
2340     ic.sendPreeditText(preeditText, preeditText.length() - 3);
2341     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2342     x = input->positionToRectangle(0).x();
2343     for (int i = 2; i >= 0; --i) {
2344         ic.sendPreeditText(preeditText, preeditText.length() - i);
2345         QCOMPARE(input->positionToRectangle(0).x(), x);
2346         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2347     }
2348     for (int i = 1; i <  3; ++i) {
2349         ic.sendPreeditText(preeditText, preeditText.length() - i);
2350         QCOMPARE(input->positionToRectangle(0).x(), x);
2351         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2352     }
2353
2354     // Test disabling auto scroll.
2355     ic.sendEvent(QInputMethodEvent());
2356
2357     input->setAutoScroll(false);
2358     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2359     QCOMPARE(input->positionAt(0), 0);
2360     QCOMPARE(input->positionAt(input->width()), 5);
2361 #endif
2362 }
2363
2364 void tst_qsgtextinput::preeditMicroFocus()
2365 {
2366 #ifdef QTBUG_21691
2367     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2368     QVERIFY(false);
2369 #else
2370     QString preeditText = "super";
2371
2372     QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputMethodEvent.qml"));
2373     MyInputContext ic;
2374     // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2375     // and QWidget won't allow an input context to be set when the flag is not set.
2376     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2377     view.setInputContext(&ic);
2378     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2379     view.show();
2380     view.requestActivateWindow();
2381     QTest::qWaitForWindowShown(&view);
2382     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2383     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2384     QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2385     QVERIFY(input);
2386
2387     QRect currentRect;
2388     QRect previousRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2389
2390     // Verify that the micro focus rect is positioned the same for position 0 as
2391     // it would be if there was no preedit text.
2392     ic.updateReceived = false;
2393     ic.sendPreeditText(preeditText, 0);
2394     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2395     QCOMPARE(currentRect, previousRect);
2396 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2397     QCOMPARE(ic.updateReceived, true);
2398 #endif
2399
2400     // Verify that the micro focus rect moves to the left as the cursor position
2401     // is incremented.
2402     for (int i = 1; i <= 5; ++i) {
2403         ic.updateReceived = false;
2404         ic.sendPreeditText(preeditText, i);
2405         currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2406         QVERIFY(previousRect.left() < currentRect.left());
2407 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2408         QCOMPARE(ic.updateReceived, true);
2409 #endif
2410         previousRect = currentRect;
2411     }
2412
2413     // Verify that if there is no preedit cursor then the micro focus rect is the
2414     // same as it would be if it were positioned at the end of the preedit text.
2415     ic.sendPreeditText(preeditText, 0);
2416     ic.updateReceived = false;
2417     ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2418     currentRect = input->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2419     QCOMPARE(currentRect, previousRect);
2420 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2421     QCOMPARE(ic.updateReceived, true);
2422 #endif
2423 #endif
2424 }
2425
2426 void tst_qsgtextinput::inputContextMouseHandler()
2427 {
2428 #ifdef QTBUG_21691
2429     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2430     QVERIFY(false);
2431 #else
2432     QString text = "supercalifragisiticexpialidocious!";
2433
2434     QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2435     MyInputContext ic;
2436     // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has active focus
2437     // and QWidget won't allow an input context to be set when the flag is not set.
2438     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2439     view.setInputContext(&ic);
2440     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2441     view.show();
2442     view.requestActivateWindow();
2443     QTest::qWaitForWindowShown(&view);
2444     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2445     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2446     QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2447     QVERIFY(input);
2448
2449     QFontMetricsF fm(input->font());
2450     const qreal y = fm.height() / 2;
2451
2452     QPoint position2 = input->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2453     QPoint position8 = input->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2454     QPoint position20 = input->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2455     QPoint position27 = input->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2456     QPoint globalPosition2 = view.mapToGlobal(position2);
2457     QPoint globalposition8 = view.mapToGlobal(position8);
2458     QPoint globalposition20 = view.mapToGlobal(position20);
2459     QPoint globalposition27 = view.mapToGlobal(position27);
2460
2461     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2462
2463     QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2464     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2465     QCOMPARE(ic.eventPosition, position2);
2466     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2467     QCOMPARE(ic.eventButton, Qt::LeftButton);
2468     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2469     QVERIFY(ic.cursor < 0);
2470     ic.eventType = QEvent::None;
2471
2472     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2473     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2474     QCOMPARE(ic.eventPosition, position2);
2475     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2476     QCOMPARE(ic.eventButton, Qt::LeftButton);
2477     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2478     QVERIFY(ic.cursor < 0);
2479     ic.eventType = QEvent::None;
2480
2481     {   QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2482         QGuiApplication::sendEvent(&view, &mv); }
2483     QCOMPARE(ic.eventType, QEvent::None);
2484
2485     {   QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2486         QGuiApplication::sendEvent(&view, &mv); }
2487     QCOMPARE(ic.eventType, QEvent::MouseMove);
2488     QCOMPARE(ic.eventPosition, position27);
2489     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2490     QCOMPARE(ic.eventButton, Qt::LeftButton);
2491     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2492     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);    // 15 is expected but some platforms may be off by one.
2493     ic.eventType = QEvent::None;
2494
2495     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2496     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2497     QCOMPARE(ic.eventPosition, position27);
2498     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2499     QCOMPARE(ic.eventButton, Qt::LeftButton);
2500     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2501     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2502     ic.eventType = QEvent::None;
2503
2504     // And in the other direction.
2505     QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2506     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2507     QCOMPARE(ic.eventPosition, position27);
2508     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2509     QCOMPARE(ic.eventButton, Qt::LeftButton);
2510     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2511     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2512     ic.eventType = QEvent::None;
2513
2514     QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2515     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2516     QCOMPARE(ic.eventPosition, position27);
2517     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2518     QCOMPARE(ic.eventButton, Qt::RightButton);
2519     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2520     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2521     ic.eventType = QEvent::None;
2522
2523     {   QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2524         QGuiApplication::sendEvent(&view, &mv); }
2525     QCOMPARE(ic.eventType, QEvent::MouseMove);
2526     QCOMPARE(ic.eventPosition, position20);
2527     QCOMPARE(ic.eventGlobalPosition, globalposition20);
2528     QCOMPARE(ic.eventButton, Qt::RightButton);
2529     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2530     QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2531     ic.eventType = QEvent::None;
2532
2533     {   QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2534         QGuiApplication::sendEvent(&view, &mv); }
2535     QCOMPARE(ic.eventType, QEvent::None);
2536
2537     QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2538     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2539     QCOMPARE(ic.eventPosition, position2);
2540     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2541     QCOMPARE(ic.eventButton, Qt::RightButton);
2542     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2543     QVERIFY(ic.cursor < 0);
2544     ic.eventType = QEvent::None;
2545 #endif
2546 }
2547
2548 void tst_qsgtextinput::inputMethodComposing()
2549 {
2550     QString text = "supercalifragisiticexpialidocious!";
2551
2552     QSGView view(QUrl::fromLocalFile(SRCDIR "/data/inputContext.qml"));
2553     view.show();
2554     view.requestActivateWindow();
2555     QTest::qWaitForWindowShown(&view);
2556     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2557     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2558     QSGTextInput *input = qobject_cast<QSGTextInput *>(view.rootObject());
2559     QVERIFY(input);
2560     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2561
2562     QCOMPARE(input->isInputMethodComposing(), false);
2563     {
2564         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2565         QGuiApplication::sendEvent(&view, &event);
2566     }
2567     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2568     QCOMPARE(input->isInputMethodComposing(), true);
2569     QCOMPARE(spy.count(), 1);
2570
2571     {
2572         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2573         QGuiApplication::sendEvent(&view, &event);
2574     }
2575     QCOMPARE(spy.count(), 1);
2576
2577     {
2578         QInputMethodEvent event;
2579         QGuiApplication::sendEvent(&view, &event);
2580     }
2581     QCOMPARE(input->isInputMethodComposing(), false);
2582     QCOMPARE(spy.count(), 2);
2583 }
2584
2585 void tst_qsgtextinput::cursorRectangleSize()
2586 {
2587 #ifdef QTBUG_21691
2588     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2589     QVERIFY(false);
2590 #else
2591     QSGView *canvas = new QSGView(QUrl::fromLocalFile(SRCDIR "/data/positionAt.qml"));
2592     QVERIFY(canvas->rootObject() != 0);
2593     canvas->show();
2594     canvas->setFocus();
2595     QGuiApplication::setActiveWindow(canvas);
2596     QTest::qWaitForWindowShown(canvas);
2597
2598     QSGTextInput *textInput = qobject_cast<QSGTextInput *>(canvas->rootObject());
2599     QVERIFY(textInput != 0);
2600     textInput->setFocus(Qt::OtherFocusReason);
2601     QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2602     QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2603     QRectF microFocusFromApp= QGuiApplication::focusWidget()->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2604
2605     QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2606     QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2607
2608     delete canvas;
2609 #endif
2610 }
2611
2612 void tst_qsgtextinput::tripleClickSelectsAll()
2613 {
2614     QString qmlfile = SRCDIR "/data/positionAt.qml";
2615     QSGView view(QUrl::fromLocalFile(qmlfile));
2616     view.requestActivateWindow();
2617     view.show();
2618     QTest::qWaitForWindowShown(&view);
2619
2620     QEXPECT_FAIL("", QTBUG_21489_MESSAGE, Continue);
2621     QTRY_COMPARE(view.windowState(), Qt::WindowActive);
2622
2623     QSGTextInput* input = qobject_cast<QSGTextInput*>(view.rootObject());
2624     QVERIFY(input);
2625
2626     QLatin1String hello("Hello world!");
2627     input->setSelectByMouse(true);
2628     input->setText(hello);
2629
2630     // Clicking on the same point inside TextInput three times in a row
2631     // should trigger a triple click, thus selecting all the text.
2632     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2633     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2634     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2635     QGuiApplication::processEvents();
2636     QCOMPARE(input->selectedText(), hello);
2637
2638     // Now it simulates user moving the mouse between the second and the third click.
2639     // In this situation, we don't expect a triple click.
2640     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2641     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2642     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2643     QGuiApplication::processEvents();
2644     QVERIFY(input->selectedText().isEmpty());
2645
2646     // And now we press the third click too late, so no triple click event is triggered.
2647     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2648     QGuiApplication::processEvents();
2649     QTest::qWait(QApplication::doubleClickInterval() + 1);
2650     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2651     QGuiApplication::processEvents();
2652     QVERIFY(input->selectedText().isEmpty());
2653 }
2654
2655 QTEST_MAIN(tst_qsgtextinput)
2656
2657 #include "tst_qsgtextinput.moc"