cae38bcd735942c08e635397841bf37637d3fa8e
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquicktextinput / tst_qquicktextinput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 <private/qinputpanel_p.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativeexpression.h>
47 #include <QFile>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputPanel>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
54 #include <QDebug>
55 #include <QDir>
56 #include <QStyle>
57 #include <QtOpenGL/QGLShaderProgram>
58 #include <math.h>
59
60 #ifdef Q_OS_MAC
61 #include <Carbon/Carbon.h>
62 #endif
63
64 #include "qplatformdefs.h"
65 #include "../../shared/platforminputcontext.h"
66
67 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
68 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
69
70 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
71 {
72     // XXX This will be replaced by some clever persistent platform image store.
73     QString persistent_dir = QDeclarativeDataTest::instance()->dataDirectory();
74     QString arch = "unknown-architecture"; // QTest needs to help with this.
75
76     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
77
78     if (!QFile::exists(expectfile)) {
79         actual.save(expectfile);
80         qWarning() << "created" << expectfile;
81     }
82
83     return expectfile;
84 }
85
86 template <typename T> static T evaluate(QObject *scope, const QString &expression)
87 {
88     QDeclarativeExpression expr(qmlContext(scope), scope, expression);
89     T result = expr.evaluate().value<T>();
90     if (expr.hasError())
91         qWarning() << expr.error().toString();
92     return result;
93 }
94
95 typedef QPair<int, QChar> Key;
96
97 class tst_qquicktextinput : public QDeclarativeDataTest
98
99 {
100     Q_OBJECT
101 public:
102     tst_qquicktextinput();
103
104 private slots:
105     void cleanup();
106     void text();
107     void width();
108     void font();
109     void color();
110     void wrap();
111     void selection();
112     void isRightToLeft_data();
113     void isRightToLeft();
114     void moveCursorSelection_data();
115     void moveCursorSelection();
116     void moveCursorSelectionSequence_data();
117     void moveCursorSelectionSequence();
118     void dragMouseSelection();
119     void mouseSelectionMode_data();
120     void mouseSelectionMode();
121     void tripleClickSelectsAll();
122
123     void horizontalAlignment_data();
124     void horizontalAlignment();
125     void horizontalAlignment_RightToLeft();
126     void verticalAlignment();
127
128     void boundingRect();
129
130     void positionAt();
131
132     void maxLength();
133     void masks();
134     void validators();
135     void inputMethods();
136
137     void passwordCharacter();
138     void cursorDelegate();
139     void cursorVisible();
140     void cursorRectangle();
141     void navigation();
142     void navigation_RTL();
143     void copyAndPaste();
144     void copyAndPasteKeySequence();
145     void canPasteEmpty();
146     void canPaste();
147     void readOnly();
148
149     void openInputPanel();
150     void setHAlignClearCache();
151     void focusOutClearSelection();
152
153     void echoMode();
154 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
155     void passwordEchoDelay();
156 #endif
157     void geometrySignals();
158     void testQtQuick11Attributes();
159     void testQtQuick11Attributes_data();
160
161     void preeditAutoScroll();
162     void preeditCursorRectangle();
163     void inputContextMouseHandler();
164     void inputMethodComposing();
165     void inputPanelUpdate();
166     void cursorRectangleSize();
167
168     void getText_data();
169     void getText();
170     void insert_data();
171     void insert();
172     void remove_data();
173     void remove();
174
175     void keySequence_data();
176     void keySequence();
177
178     void undo_data();
179     void undo();
180     void redo_data();
181     void redo();
182     void undo_keypressevents_data();
183     void undo_keypressevents();
184
185     void QTBUG_19956();
186     void QTBUG_19956_data();
187     void QTBUG_19956_regexp();
188
189     void negativeDimensions();
190
191 private:
192     void simulateKey(QQuickView *, int key);
193
194     void simulateKeys(QWindow *window, const QList<Key> &keys);
195     void simulateKeys(QWindow *window, const QKeySequence &sequence);
196
197     QDeclarativeEngine engine;
198     QStringList standard;
199     QStringList colorStrings;
200 };
201
202 typedef QList<int> IntList;
203 Q_DECLARE_METATYPE(IntList)
204
205 typedef QList<Key> KeyList;
206 Q_DECLARE_METATYPE(KeyList)
207
208 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
209 {
210     for (int i = 0; i < keys.count(); ++i) {
211         const int key = keys.at(i).first;
212         const int modifiers = key & Qt::KeyboardModifierMask;
213         const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
214
215         QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
216         QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
217
218         QGuiApplication::sendEvent(window, &press);
219         QGuiApplication::sendEvent(window, &release);
220     }
221 }
222
223 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
224 {
225     for (uint i = 0; i < sequence.count(); ++i) {
226         const int key = sequence[i];
227         const int modifiers = key & Qt::KeyboardModifierMask;
228
229         QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
230     }
231 }
232
233 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
234 {
235     for (uint i = 0; i < sequence.count(); ++i)
236         keys << Key(sequence[i], QChar());
237     return keys;
238 }
239
240 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
241 {
242     for (int i = 0; i < N - 1; ++i) {
243         int key = QTest::asciiToKey(characters[i]);
244         QChar character = QLatin1Char(characters[i]);
245         keys << Key(key, character);
246     }
247     return keys;
248 }
249
250 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
251 {
252     keys << Key(key, QChar());
253     return keys;
254 }
255
256 void tst_qquicktextinput::cleanup()
257 {
258     // ensure not even skipped tests with custom input context leave it dangling
259     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
260     inputPanelPrivate->testContext = 0;
261 }
262
263 tst_qquicktextinput::tst_qquicktextinput()
264 {
265     standard << "the quick brown fox jumped over the lazy dog"
266         << "It's supercalifragisiticexpialidocious!"
267         << "Hello, world!"
268         << "!dlrow ,olleH"
269         << " spacey   text ";
270
271     colorStrings << "aliceblue"
272                  << "antiquewhite"
273                  << "aqua"
274                  << "darkkhaki"
275                  << "darkolivegreen"
276                  << "dimgray"
277                  << "palevioletred"
278                  << "lightsteelblue"
279                  << "#000000"
280                  << "#AAAAAA"
281                  << "#FFFFFF"
282                  << "#2AC05F";
283 }
284
285 void tst_qquicktextinput::text()
286 {
287     {
288         QDeclarativeComponent textinputComponent(&engine);
289         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\"  }", QUrl());
290         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
291
292         QVERIFY(textinputObject != 0);
293         QCOMPARE(textinputObject->text(), QString(""));
294         QCOMPARE(textinputObject->length(), 0);
295
296         delete textinputObject;
297     }
298
299     for (int i = 0; i < standard.size(); i++)
300     {
301         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
302         QDeclarativeComponent textinputComponent(&engine);
303         textinputComponent.setData(componentStr.toLatin1(), QUrl());
304         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
305
306         QVERIFY(textinputObject != 0);
307         QCOMPARE(textinputObject->text(), standard.at(i));
308         QCOMPARE(textinputObject->length(), standard.at(i).length());
309
310         delete textinputObject;
311     }
312
313 }
314
315 void tst_qquicktextinput::width()
316 {
317     // uses Font metrics to find the width for standard
318     {
319         QDeclarativeComponent textinputComponent(&engine);
320         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\" }", QUrl());
321         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
322
323         QVERIFY(textinputObject != 0);
324         QCOMPARE(textinputObject->width(), 0.0);
325
326         delete textinputObject;
327     }
328
329     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
330
331     for (int i = 0; i < standard.size(); i++)
332     {
333         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
334         QDeclarativeComponent textinputComponent(&engine);
335         textinputComponent.setData(componentStr.toLatin1(), QUrl());
336         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
337
338         QString s = standard.at(i);
339         s.replace(QLatin1Char('\n'), QChar::LineSeparator);
340
341         QTextLayout layout(s);
342         layout.setFont(textinputObject->font());
343         layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
344         if (requiresUnhintedMetrics) {
345             QTextOption option;
346             option.setUseDesignMetrics(true);
347             layout.setTextOption(option);
348         }
349
350         layout.beginLayout();
351         forever {
352             QTextLine line = layout.createLine();
353             if (!line.isValid())
354                 break;
355         }
356
357         layout.endLayout();
358
359         qreal metricWidth = ceil(layout.boundingRect().width());
360
361         QVERIFY(textinputObject != 0);
362         int delta = abs(int(int(textinputObject->width()) - metricWidth));
363         QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
364
365         delete textinputObject;
366     }
367 }
368
369 void tst_qquicktextinput::font()
370 {
371     //test size, then bold, then italic, then family
372     {
373         QString componentStr = "import QtQuick 2.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
374         QDeclarativeComponent textinputComponent(&engine);
375         textinputComponent.setData(componentStr.toLatin1(), QUrl());
376         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
377
378         QVERIFY(textinputObject != 0);
379         QCOMPARE(textinputObject->font().pointSize(), 40);
380         QCOMPARE(textinputObject->font().bold(), false);
381         QCOMPARE(textinputObject->font().italic(), false);
382
383         delete textinputObject;
384     }
385
386     {
387         QString componentStr = "import QtQuick 2.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
388         QDeclarativeComponent textinputComponent(&engine);
389         textinputComponent.setData(componentStr.toLatin1(), QUrl());
390         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
391
392         QVERIFY(textinputObject != 0);
393         QCOMPARE(textinputObject->font().bold(), true);
394         QCOMPARE(textinputObject->font().italic(), false);
395
396         delete textinputObject;
397     }
398
399     {
400         QString componentStr = "import QtQuick 2.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
401         QDeclarativeComponent textinputComponent(&engine);
402         textinputComponent.setData(componentStr.toLatin1(), QUrl());
403         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
404
405         QVERIFY(textinputObject != 0);
406         QCOMPARE(textinputObject->font().italic(), true);
407         QCOMPARE(textinputObject->font().bold(), false);
408
409         delete textinputObject;
410     }
411
412     {
413         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
414         QDeclarativeComponent textinputComponent(&engine);
415         textinputComponent.setData(componentStr.toLatin1(), QUrl());
416         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
417
418         QVERIFY(textinputObject != 0);
419         QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
420         QCOMPARE(textinputObject->font().bold(), false);
421         QCOMPARE(textinputObject->font().italic(), false);
422
423         delete textinputObject;
424     }
425
426     {
427         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
428         QDeclarativeComponent textinputComponent(&engine);
429         textinputComponent.setData(componentStr.toLatin1(), QUrl());
430         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
431
432         QVERIFY(textinputObject != 0);
433         QCOMPARE(textinputObject->font().family(), QString(""));
434
435         delete textinputObject;
436     }
437 }
438
439 void tst_qquicktextinput::color()
440 {
441     //test color
442     for (int i = 0; i < colorStrings.size(); i++)
443     {
444         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
445         QDeclarativeComponent textinputComponent(&engine);
446         textinputComponent.setData(componentStr.toLatin1(), QUrl());
447         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
448         QVERIFY(textinputObject != 0);
449         QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
450
451         delete textinputObject;
452     }
453
454     //test selection color
455     for (int i = 0; i < colorStrings.size(); i++)
456     {
457         QString componentStr = "import QtQuick 2.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
458         QDeclarativeComponent textinputComponent(&engine);
459         textinputComponent.setData(componentStr.toLatin1(), QUrl());
460         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
461         QVERIFY(textinputObject != 0);
462         QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
463
464         delete textinputObject;
465     }
466
467     //test selected text color
468     for (int i = 0; i < colorStrings.size(); i++)
469     {
470         QString componentStr = "import QtQuick 2.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
471         QDeclarativeComponent textinputComponent(&engine);
472         textinputComponent.setData(componentStr.toLatin1(), QUrl());
473         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
474         QVERIFY(textinputObject != 0);
475         QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
476
477         delete textinputObject;
478     }
479
480     {
481         QString colorStr = "#AA001234";
482         QColor testColor("#001234");
483         testColor.setAlpha(170);
484
485         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
486         QDeclarativeComponent textinputComponent(&engine);
487         textinputComponent.setData(componentStr.toLatin1(), QUrl());
488         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
489
490         QVERIFY(textinputObject != 0);
491         QCOMPARE(textinputObject->color(), testColor);
492
493         delete textinputObject;
494     }
495 }
496
497 void tst_qquicktextinput::wrap()
498 {
499     int textHeight = 0;
500     // for specified width and wrap set true
501     {
502         QDeclarativeComponent textComponent(&engine);
503         textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
504         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
505         textHeight = textObject->height();
506
507         QVERIFY(textObject != 0);
508         QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
509         QCOMPARE(textObject->width(), 300.);
510
511         delete textObject;
512     }
513
514     for (int i = 0; i < standard.count(); i++) {
515         QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
516         QDeclarativeComponent textComponent(&engine);
517         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
518         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
519
520         QVERIFY(textObject != 0);
521         QCOMPARE(textObject->width(), 30.);
522         QVERIFY(textObject->height() > textHeight);
523
524         int oldHeight = textObject->height();
525         textObject->setWidth(100);
526         QVERIFY(textObject->height() < oldHeight);
527
528         delete textObject;
529     }
530 }
531
532 void tst_qquicktextinput::selection()
533 {
534     QString testStr = standard[0];
535     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
536     QDeclarativeComponent textinputComponent(&engine);
537     textinputComponent.setData(componentStr.toLatin1(), QUrl());
538     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
539     QVERIFY(textinputObject != 0);
540
541
542     //Test selection follows cursor
543     for (int i=0; i<= testStr.size(); i++) {
544         textinputObject->setCursorPosition(i);
545         QCOMPARE(textinputObject->cursorPosition(), i);
546         QCOMPARE(textinputObject->selectionStart(), i);
547         QCOMPARE(textinputObject->selectionEnd(), i);
548         QVERIFY(textinputObject->selectedText().isNull());
549     }
550
551     textinputObject->setCursorPosition(0);
552     QVERIFY(textinputObject->cursorPosition() == 0);
553     QVERIFY(textinputObject->selectionStart() == 0);
554     QVERIFY(textinputObject->selectionEnd() == 0);
555     QVERIFY(textinputObject->selectedText().isNull());
556
557     // Verify invalid positions are ignored.
558     textinputObject->setCursorPosition(-1);
559     QVERIFY(textinputObject->cursorPosition() == 0);
560     QVERIFY(textinputObject->selectionStart() == 0);
561     QVERIFY(textinputObject->selectionEnd() == 0);
562     QVERIFY(textinputObject->selectedText().isNull());
563
564     textinputObject->setCursorPosition(textinputObject->text().count()+1);
565     QVERIFY(textinputObject->cursorPosition() == 0);
566     QVERIFY(textinputObject->selectionStart() == 0);
567     QVERIFY(textinputObject->selectionEnd() == 0);
568     QVERIFY(textinputObject->selectedText().isNull());
569
570     //Test selection
571     for (int i=0; i<= testStr.size(); i++) {
572         textinputObject->select(0,i);
573         QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
574     }
575     for (int i=0; i<= testStr.size(); i++) {
576         textinputObject->select(i,testStr.size());
577         QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
578     }
579
580     textinputObject->setCursorPosition(0);
581     QVERIFY(textinputObject->cursorPosition() == 0);
582     QVERIFY(textinputObject->selectionStart() == 0);
583     QVERIFY(textinputObject->selectionEnd() == 0);
584     QVERIFY(textinputObject->selectedText().isNull());
585
586     //Test Error Ignoring behaviour
587     textinputObject->setCursorPosition(0);
588     QVERIFY(textinputObject->selectedText().isNull());
589     textinputObject->select(-10,0);
590     QVERIFY(textinputObject->selectedText().isNull());
591     textinputObject->select(100,110);
592     QVERIFY(textinputObject->selectedText().isNull());
593     textinputObject->select(0,-10);
594     QVERIFY(textinputObject->selectedText().isNull());
595     textinputObject->select(0,100);
596     QVERIFY(textinputObject->selectedText().isNull());
597     textinputObject->select(0,10);
598     QVERIFY(textinputObject->selectedText().size() == 10);
599     textinputObject->select(-10,10);
600     QVERIFY(textinputObject->selectedText().size() == 10);
601     textinputObject->select(100,101);
602     QVERIFY(textinputObject->selectedText().size() == 10);
603     textinputObject->select(0,-10);
604     QVERIFY(textinputObject->selectedText().size() == 10);
605     textinputObject->select(0,100);
606     QVERIFY(textinputObject->selectedText().size() == 10);
607
608     textinputObject->deselect();
609     QVERIFY(textinputObject->selectedText().isNull());
610     textinputObject->select(0,10);
611     QVERIFY(textinputObject->selectedText().size() == 10);
612     textinputObject->deselect();
613     QVERIFY(textinputObject->selectedText().isNull());
614
615     // test input method selection
616     QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
617     textinputObject->setFocus(true);
618     {
619         QList<QInputMethodEvent::Attribute> attributes;
620         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
621         QInputMethodEvent event("", attributes);
622         QGuiApplication::sendEvent(textinputObject, &event);
623     }
624     QCOMPARE(selectionSpy.count(), 1);
625     QCOMPARE(textinputObject->selectionStart(), 12);
626     QCOMPARE(textinputObject->selectionEnd(), 17);
627
628     delete textinputObject;
629 }
630
631 void tst_qquicktextinput::isRightToLeft_data()
632 {
633     QTest::addColumn<QString>("text");
634     QTest::addColumn<bool>("emptyString");
635     QTest::addColumn<bool>("firstCharacter");
636     QTest::addColumn<bool>("lastCharacter");
637     QTest::addColumn<bool>("middleCharacter");
638     QTest::addColumn<bool>("startString");
639     QTest::addColumn<bool>("midString");
640     QTest::addColumn<bool>("endString");
641
642     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
643     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
644     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
645     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
646     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
647     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;
648     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
649 }
650
651 void tst_qquicktextinput::isRightToLeft()
652 {
653     QFETCH(QString, text);
654     QFETCH(bool, emptyString);
655     QFETCH(bool, firstCharacter);
656     QFETCH(bool, lastCharacter);
657     QFETCH(bool, middleCharacter);
658     QFETCH(bool, startString);
659     QFETCH(bool, midString);
660     QFETCH(bool, endString);
661
662     QQuickTextInput textInput;
663     textInput.setText(text);
664
665     // first test that the right string is delivered to the QString::isRightToLeft()
666     QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
667     QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
668     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
669     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
670     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
671     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
672     if (text.isEmpty())
673         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
674     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
675
676     // then test that the feature actually works
677     QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
678     QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
679     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
680     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
681     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
682     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
683     if (text.isEmpty())
684         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
685     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
686 }
687
688 void tst_qquicktextinput::moveCursorSelection_data()
689 {
690     QTest::addColumn<QString>("testStr");
691     QTest::addColumn<int>("cursorPosition");
692     QTest::addColumn<int>("movePosition");
693     QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
694     QTest::addColumn<int>("selectionStart");
695     QTest::addColumn<int>("selectionEnd");
696     QTest::addColumn<bool>("reversible");
697
698     // () contains the text selected by the cursor.
699     // <> contains the actual selection.
700
701     QTest::newRow("(t)he|characters")
702             << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
703     QTest::newRow("do(g)|characters")
704             << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
705     QTest::newRow("jum(p)ed|characters")
706             << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
707     QTest::newRow("jumped( )over|characters")
708             << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
709     QTest::newRow("(the )|characters")
710             << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
711     QTest::newRow("( dog)|characters")
712             << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
713     QTest::newRow("( jumped )|characters")
714             << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
715     QTest::newRow("th(e qu)ick|characters")
716             << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
717     QTest::newRow("la(zy d)og|characters")
718             << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
719     QTest::newRow("jum(ped ov)er|characters")
720             << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
721     QTest::newRow("()the|characters")
722             << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
723     QTest::newRow("dog()|characters")
724             << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
725     QTest::newRow("jum()ped|characters")
726             << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
727
728     QTest::newRow("<(t)he>|words")
729             << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
730     QTest::newRow("<do(g)>|words")
731             << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
732     QTest::newRow("<jum(p)ed>|words")
733             << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
734     QTest::newRow("<jumped( )>over|words,ltr")
735             << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
736     QTest::newRow("jumped<( )over>|words,rtl")
737             << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
738     QTest::newRow("<(the )>quick|words,ltr")
739             << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
740     QTest::newRow("<(the )quick>|words,rtl")
741             << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
742     QTest::newRow("<lazy( dog)>|words,ltr")
743             << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
744     QTest::newRow("lazy<( dog)>|words,rtl")
745             << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
746     QTest::newRow("<fox( jumped )>over|words,ltr")
747             << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
748     QTest::newRow("fox<( jumped )over>|words,rtl")
749             << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
750     QTest::newRow("<th(e qu)ick>|words")
751             << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
752     QTest::newRow("<la(zy d)og|words>")
753             << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
754     QTest::newRow("<jum(ped ov)er>|words")
755             << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
756     QTest::newRow("<()>the|words")
757             << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
758     QTest::newRow("dog<()>|words")
759             << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
760     QTest::newRow("jum<()>ped|words")
761             << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
762
763     QTest::newRow("Hello<(,)> |words")
764             << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
765     QTest::newRow("Hello<(, )>world|words,ltr")
766             << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
767     QTest::newRow("Hello<(, )world>|words,rtl")
768             << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
769     QTest::newRow("<Hel(lo, )>world|words,ltr")
770             << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
771     QTest::newRow("<Hel(lo, )world>|words,rtl")
772             << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
773     QTest::newRow("<Hel(lo)>,|words")
774             << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
775     QTest::newRow("Hello<()>,|words")
776             << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
777     QTest::newRow("Hello,<()>|words")
778             << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
779     QTest::newRow("Hello<,( )>world|words,ltr")
780             << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
781     QTest::newRow("Hello,<( )world>|words,rtl")
782             << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
783     QTest::newRow("Hello<,( world)>|words,ltr")
784             << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
785     QTest::newRow("Hello,<( world)>|words,rtl")
786             << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
787     QTest::newRow("Hello<,( world!)>|words,ltr")
788             << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
789     QTest::newRow("Hello,<( world!)>|words,rtl")
790             << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
791     QTest::newRow("Hello<(, world!)>|words")
792             << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
793     // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
794     // QTBUG-11365
795     // QTest::newRow("world<(!)>|words")
796     //         << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
797     QTest::newRow("world!<()>)|words")
798             << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
799     QTest::newRow("world<()>!)|words")
800             << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
801
802     QTest::newRow("<(,)>olleH |words")
803             << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
804     QTest::newRow("<dlrow( ,)>olleH|words,ltr")
805             << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
806     QTest::newRow("dlrow<( ,)>olleH|words,rtl")
807             << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
808     QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
809             << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
810     QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
811             << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
812     QTest::newRow(",<(ol)leH>,|words")
813             << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
814     QTest::newRow(",<()>olleH|words")
815             << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
816     QTest::newRow("<()>,olleH|words")
817             << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
818     QTest::newRow("<dlrow( )>,olleH|words,ltr")
819             << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
820     QTest::newRow("dlrow<( ),>olleH|words,rtl")
821             << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
822     QTest::newRow("<(dlrow )>,olleH|words,ltr")
823             << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
824     QTest::newRow("<(dlrow ),>olleH|words,rtl")
825             << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
826     QTest::newRow("<(!dlrow )>,olleH|words,ltr")
827             << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
828     QTest::newRow("<(!dlrow ),>olleH|words,rtl")
829             << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
830     QTest::newRow("(!dlrow ,)olleH|words")
831             << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
832     QTest::newRow("<(!)>dlrow|words")
833             << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
834     QTest::newRow("<()>!dlrow|words")
835             << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
836     QTest::newRow("!<()>dlrow|words")
837             << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
838
839     QTest::newRow(" <s(pac)ey>   text |words")
840             << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
841     QTest::newRow(" spacey   <t(ex)t> |words")
842             << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
843     QTest::newRow("<( )>spacey   text |words|ltr")
844             << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
845     QTest::newRow("<( )spacey>   text |words|rtl")
846             << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
847     QTest::newRow("spacey   <text( )>|words|ltr")
848             << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
849 //    QTBUG-11365
850 //    QTest::newRow("spacey   text<( )>|words|rtl")
851 //            << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
852     QTest::newRow("<()> spacey   text |words")
853             << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
854     QTest::newRow(" spacey   text <()>|words")
855             << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
856 }
857
858 void tst_qquicktextinput::moveCursorSelection()
859 {
860     QFETCH(QString, testStr);
861     QFETCH(int, cursorPosition);
862     QFETCH(int, movePosition);
863     QFETCH(QQuickTextInput::SelectionMode, mode);
864     QFETCH(int, selectionStart);
865     QFETCH(int, selectionEnd);
866     QFETCH(bool, reversible);
867
868     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
869     QDeclarativeComponent textinputComponent(&engine);
870     textinputComponent.setData(componentStr.toLatin1(), QUrl());
871     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
872     QVERIFY(textinputObject != 0);
873
874     textinputObject->setCursorPosition(cursorPosition);
875     textinputObject->moveCursorSelection(movePosition, mode);
876
877     QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
878     QCOMPARE(textinputObject->selectionStart(), selectionStart);
879     QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
880
881     if (reversible) {
882         textinputObject->setCursorPosition(movePosition);
883         textinputObject->moveCursorSelection(cursorPosition, mode);
884
885         QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
886         QCOMPARE(textinputObject->selectionStart(), selectionStart);
887         QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
888     }
889
890     delete textinputObject;
891 }
892
893 void tst_qquicktextinput::moveCursorSelectionSequence_data()
894 {
895     QTest::addColumn<QString>("testStr");
896     QTest::addColumn<int>("cursorPosition");
897     QTest::addColumn<int>("movePosition1");
898     QTest::addColumn<int>("movePosition2");
899     QTest::addColumn<int>("selection1Start");
900     QTest::addColumn<int>("selection1End");
901     QTest::addColumn<int>("selection2Start");
902     QTest::addColumn<int>("selection2End");
903
904     // () contains the text selected by the cursor.
905     // <> contains the actual selection.
906     // ^ is the revised cursor position.
907     // {} contains the revised selection.
908
909     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
910             << standard[0]
911             << 9 << 13 << 17
912             << 4 << 15
913             << 4 << 19;
914     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
915             << standard[0]
916             << 13 << 9 << 17
917             << 9 << 15
918             << 10 << 19;
919     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
920             << standard[0]
921             << 9 << 13 << 16
922             << 4 << 15
923             << 4 << 16;
924     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
925             << standard[0]
926             << 13 << 9 << 16
927             << 9 << 15
928             << 10 << 16;
929     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
930             << standard[0]
931             << 9 << 13 << 15
932             << 4 << 15
933             << 4 << 15;
934     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
935             << standard[0]
936             << 13 << 9 << 15
937             << 9 << 15
938             << 10 << 15;
939     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
940             << standard[0]
941             << 9 << 13 << 10
942             << 4 << 15
943             << 4 << 10;
944     QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
945             << standard[0]
946             << 13 << 9 << 10
947             << 9 << 15
948             << 10 << 15;
949     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
950             << standard[0]
951             << 9 << 13 << 9
952             << 4 << 15
953             << 4 << 9;
954     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
955             << standard[0]
956             << 13 << 9 << 9
957             << 9 << 15
958             << 9 << 15;
959     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
960             << standard[0]
961             << 9 << 13 << 7
962             << 4 << 15
963             << 4 << 9;
964     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
965             << standard[0]
966             << 13 << 9 << 7
967             << 9 << 15
968             << 4 << 15;
969     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
970             << standard[0]
971             << 9 << 13 << 4
972             << 4 << 15
973             << 4 << 9;
974     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
975             << standard[0]
976             << 13 << 9 << 4
977             << 9 << 15
978             << 4 << 15;
979     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
980             << standard[0]
981             << 9 << 13 << 3
982             << 4 << 15
983             << 3 << 9;
984     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
985             << standard[0]
986             << 13 << 9 << 3
987             << 9 << 15
988             << 3 << 15;
989     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
990             << standard[0]
991             << 9 << 13 << 1
992             << 4 << 15
993             << 0 << 9;
994     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
995             << standard[0]
996             << 13 << 9 << 1
997             << 9 << 15
998             << 0 << 15;
999
1000     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1001             << standard[2]
1002             << 2 << 4 << 8
1003             << 0 << 5
1004             << 0 << 12;
1005     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1006             << standard[2]
1007             << 4 << 2 << 8
1008             << 0 << 5
1009             << 0 << 12;
1010
1011     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1012             << standard[3]
1013             << 9 << 11 << 5
1014             << 8 << 13
1015             << 1 << 13;
1016     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1017             << standard[3]
1018             << 11 << 9 << 5
1019             << 8 << 13
1020             << 1 << 13;
1021
1022     QTest::newRow("{<(^} sp)acey>   text |ltr")
1023             << standard[4]
1024             << 0 << 3 << 0
1025             << 0 << 7
1026             << 0 << 0;
1027     QTest::newRow("{<( ^}sp)acey>   text |ltr")
1028             << standard[4]
1029             << 0 << 3 << 1
1030             << 0 << 7
1031             << 0 << 1;
1032     QTest::newRow("<( {s^p)acey>}   text |rtl")
1033             << standard[4]
1034             << 3 << 0 << 2
1035             << 0 << 7
1036             << 1 << 7;
1037     QTest::newRow("<( {^sp)acey>}   text |rtl")
1038             << standard[4]
1039             << 3 << 0 << 1
1040             << 0 << 7
1041             << 1 << 7;
1042
1043     QTest::newRow(" spacey   <te(xt {^)>}|rtl")
1044             << standard[4]
1045             << 15 << 12 << 15
1046             << 10 << 15
1047             << 15 << 15;
1048 //    QTBUG-11365
1049 //    QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
1050 //            << standard[4]
1051 //            << 15 << 12 << 14
1052 //            << 10 << 15
1053 //            << 14 << 15;
1054     QTest::newRow(" spacey   {<te(x^t} )>|ltr")
1055             << standard[4]
1056             << 12 << 15 << 13
1057             << 10 << 15
1058             << 10 << 14;
1059 //    QTBUG-11365
1060 //    QTest::newRow(" spacey   {<te(xt^} )>|ltr")
1061 //            << standard[4]
1062 //            << 12 << 15 << 14
1063 //            << 10 << 15
1064 //            << 10 << 14;
1065 }
1066
1067 void tst_qquicktextinput::moveCursorSelectionSequence()
1068 {
1069     QFETCH(QString, testStr);
1070     QFETCH(int, cursorPosition);
1071     QFETCH(int, movePosition1);
1072     QFETCH(int, movePosition2);
1073     QFETCH(int, selection1Start);
1074     QFETCH(int, selection1End);
1075     QFETCH(int, selection2Start);
1076     QFETCH(int, selection2End);
1077
1078     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
1079     QDeclarativeComponent textinputComponent(&engine);
1080     textinputComponent.setData(componentStr.toLatin1(), QUrl());
1081     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1082     QVERIFY(textinputObject != 0);
1083
1084     textinputObject->setCursorPosition(cursorPosition);
1085
1086     textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1087     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1088     QCOMPARE(textinputObject->selectionStart(), selection1Start);
1089     QCOMPARE(textinputObject->selectionEnd(), selection1End);
1090
1091     textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1092     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1093     QCOMPARE(textinputObject->selectionStart(), selection2Start);
1094     QCOMPARE(textinputObject->selectionEnd(), selection2End);
1095
1096     delete textinputObject;
1097 }
1098
1099 void tst_qquicktextinput::dragMouseSelection()
1100 {
1101     QString qmlfile = testFile("mouseselection_true.qml");
1102
1103     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1104
1105     canvas.show();
1106     canvas.requestActivateWindow();
1107     QTest::qWaitForWindowShown(&canvas);
1108
1109     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1110
1111     QVERIFY(canvas.rootObject() != 0);
1112     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1113     QVERIFY(textInputObject != 0);
1114
1115     // press-and-drag-and-release from x1 to x2
1116     int x1 = 10;
1117     int x2 = 70;
1118     int y = textInputObject->height()/2;
1119     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1120     QTest::mouseMove(&canvas, QPoint(x2, y));
1121     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1122     QTest::qWait(100);
1123     QString str1;
1124     QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1125     QVERIFY(str1.length() > 3);
1126
1127     // press and drag the current selection.
1128     x1 = 40;
1129     x2 = 100;
1130     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1131     QTest::mouseMove(&canvas, QPoint(x2, y));
1132     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1133     QTest::qWait(300);
1134     QString str2 = textInputObject->selectedText();
1135     QVERIFY(str2.length() > 3);
1136
1137     QVERIFY(str1 != str2);
1138 }
1139
1140 void tst_qquicktextinput::mouseSelectionMode_data()
1141 {
1142     QTest::addColumn<QString>("qmlfile");
1143     QTest::addColumn<bool>("selectWords");
1144
1145     // import installed
1146     QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1147     QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1148     QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1149 }
1150
1151 void tst_qquicktextinput::mouseSelectionMode()
1152 {
1153     QFETCH(QString, qmlfile);
1154     QFETCH(bool, selectWords);
1155
1156     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1157
1158     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1159
1160     canvas.show();
1161     canvas.requestActivateWindow();
1162     QTest::qWaitForWindowShown(&canvas);
1163     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1164
1165     QVERIFY(canvas.rootObject() != 0);
1166     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1167     QVERIFY(textInputObject != 0);
1168
1169     // press-and-drag-and-release from x1 to x2
1170     int x1 = 10;
1171     int x2 = 70;
1172     int y = textInputObject->height()/2;
1173     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1174     QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1175     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1176     QTest::qWait(300);
1177     if (selectWords) {
1178         QTRY_COMPARE(textInputObject->selectedText(), text);
1179     } else {
1180         QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1181         QVERIFY(textInputObject->selectedText() != text);
1182     }
1183 }
1184
1185 void tst_qquicktextinput::horizontalAlignment_data()
1186 {
1187     QTest::addColumn<int>("hAlign");
1188     QTest::addColumn<QString>("expectfile");
1189
1190     QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1191     QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1192     QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1193 }
1194
1195 void tst_qquicktextinput::horizontalAlignment()
1196 {
1197     QSKIP("Image comparison of text is almost guaranteed to fail during development");
1198
1199     QFETCH(int, hAlign);
1200     QFETCH(QString, expectfile);
1201
1202     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1203
1204     canvas.show();
1205     canvas.requestActivateWindow();
1206     QTest::qWaitForWindowShown(&canvas);
1207     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1208     QObject *ob = canvas.rootObject();
1209     QVERIFY(ob != 0);
1210     ob->setProperty("horizontalAlignment",hAlign);
1211     QImage actual = canvas.grabFrameBuffer();
1212
1213     expectfile = createExpectedFileIfNotFound(expectfile, actual);
1214
1215     QImage expect(expectfile);
1216
1217     QCOMPARE(actual,expect);
1218 }
1219
1220 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1221 {
1222     PlatformInputContext platformInputContext;
1223     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
1224     inputPanelPrivate->testContext = &platformInputContext;
1225
1226     QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1227     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1228     QVERIFY(textInput != 0);
1229     canvas.show();
1230
1231     const QString rtlText = textInput->text();
1232
1233     QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1234     QVERIFY(textInputPrivate != 0);
1235     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1236     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1237
1238     // implicit alignment should follow the reading direction of RTL text
1239     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1240     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1241     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1242     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1243
1244     // explicitly left aligned
1245     textInput->setHAlign(QQuickTextInput::AlignLeft);
1246     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1247     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1248     QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1249
1250     // explicitly right aligned
1251     textInput->setHAlign(QQuickTextInput::AlignRight);
1252     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1253     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1254     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1255     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1256
1257     // explicitly center aligned
1258     textInput->setHAlign(QQuickTextInput::AlignHCenter);
1259     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1260     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1261     QVERIFY(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll > 0);
1262     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll < textInput->width());
1263
1264     // reseted alignment should go back to following the text reading direction
1265     textInput->resetHAlign();
1266     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1267     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1268     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1269     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1270
1271     // mirror the text item
1272     QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1273
1274     // mirrored implicit alignment should continue to follow the reading direction of the text
1275     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1276     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1277     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1278     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1279
1280     // explicitly right aligned behaves as left aligned
1281     textInput->setHAlign(QQuickTextInput::AlignRight);
1282     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1283     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1284     QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1285
1286     // mirrored explicitly left aligned behaves as right aligned
1287     textInput->setHAlign(QQuickTextInput::AlignLeft);
1288     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1289     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1290     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1291     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1292
1293     // disable mirroring
1294     QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1295     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1296     textInput->resetHAlign();
1297
1298     // English text should be implicitly left aligned
1299     textInput->setText("Hello world!");
1300     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1301     QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1302
1303     canvas.requestActivateWindow();
1304     QTest::qWaitForWindowShown(&canvas);
1305     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1306
1307     // If there is no commited text, the preedit text should determine the alignment.
1308     textInput->setText(QString());
1309     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1310     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1311     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1312     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1313
1314     // Clear pre-edit text.  TextInput should maybe do this itself on setText, but that may be
1315     // redundant as an actual input method may take care of it.
1316     { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1317
1318     // empty text with implicit alignment follows the system locale-based
1319     // keyboard input direction from QInputPanel::inputDirection()
1320     textInput->setText("");
1321     platformInputContext.setInputDirection(Qt::LeftToRight);
1322     QVERIFY(qApp->inputPanel()->inputDirection() == Qt::LeftToRight);
1323     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1324     QCOMPARE(textInputPrivate->boundingRect.left() - textInputPrivate->hscroll, qreal(0));
1325
1326     QSignalSpy cursorRectangleSpy(textInput, SIGNAL(cursorRectangleChanged()));
1327     platformInputContext.setInputDirection(Qt::RightToLeft);
1328     QVERIFY(qApp->inputPanel()->inputDirection() == Qt::RightToLeft);
1329     QCOMPARE(cursorRectangleSpy.count(), 1);
1330     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1331     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1332     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1333
1334     // set input direction while having content
1335     platformInputContext.setInputDirection(Qt::LeftToRight);
1336     textInput->setText("a");
1337     platformInputContext.setInputDirection(Qt::RightToLeft);
1338     QTest::keyClick(&canvas, Qt::Key_Backspace);
1339     QVERIFY(textInput->text().isEmpty());
1340     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1341     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1342     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1343
1344     // input direction changed while not having focus
1345     platformInputContext.setInputDirection(Qt::LeftToRight);
1346     textInput->setFocus(false);
1347     platformInputContext.setInputDirection(Qt::RightToLeft);
1348     textInput->setFocus(true);
1349     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1350     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1351     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1352
1353     textInput->setHAlign(QQuickTextInput::AlignRight);
1354     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1355     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll >= textInput->width() - 1);
1356     QVERIFY(textInputPrivate->boundingRect.right() - textInputPrivate->hscroll <= textInput->width() + 1);
1357 }
1358
1359 void tst_qquicktextinput::verticalAlignment()
1360 {
1361     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1362     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1363     QVERIFY(textInput != 0);
1364     canvas.show();
1365
1366     QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1367     QVERIFY(textInputPrivate != 0);
1368
1369     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1370     QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll < canvas.height() / 2);
1371
1372     // bottom aligned
1373     textInput->setVAlign(QQuickTextInput::AlignBottom);
1374     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1375     QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll > canvas.height() / 2);
1376
1377     // explicitly center aligned
1378     textInput->setVAlign(QQuickTextInput::AlignVCenter);
1379     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1380     QVERIFY(textInputPrivate->boundingRect.top() - textInputPrivate->vscroll < canvas.height() / 2);
1381     QVERIFY(textInputPrivate->boundingRect.bottom() - textInputPrivate->vscroll > canvas.height() / 2);
1382 }
1383
1384 void tst_qquicktextinput::boundingRect()
1385 {
1386     QDeclarativeComponent component(&engine);
1387     component.setData("import QtQuick 2.0\n TextInput {}", QUrl());
1388     QScopedPointer<QObject> object(component.create());
1389     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data());
1390     QVERIFY(input);
1391
1392     QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1393     QCOMPARE(input->height(), input->boundingRect().height());
1394
1395     input->setText("Hello World");
1396     QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1397     QCOMPARE(input->height(), input->boundingRect().height());
1398
1399     // bounding rect shouldn't exceed the size of the item, expect for the cursor width;
1400     input->setWidth(input->width() / 2);
1401     QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1402     QCOMPARE(input->height(), input->boundingRect().height());
1403
1404     input->setHeight(input->height() * 2);
1405     QCOMPARE(input->width() + input->cursorRectangle().width(), input->boundingRect().width());
1406     QCOMPARE(input->height(), input->boundingRect().height());
1407
1408     QDeclarativeComponent cursorComponent(&engine);
1409     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
1410
1411     input->setCursorDelegate(&cursorComponent);
1412
1413     // If a cursor delegate is used it's size should determine the excess width.
1414     QCOMPARE(input->width() + 8, input->boundingRect().width());
1415     QCOMPARE(input->height(), input->boundingRect().height());
1416 }
1417
1418 void tst_qquicktextinput::positionAt()
1419 {
1420     QQuickView canvas(testFileUrl("positionAt.qml"));
1421     QVERIFY(canvas.rootObject() != 0);
1422     canvas.show();
1423     canvas.requestActivateWindow();
1424     QTest::qWaitForWindowShown(&canvas);
1425
1426     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1427     QVERIFY(textinputObject != 0);
1428
1429     // Check autoscrolled...
1430
1431     int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1432
1433     QTextLayout layout(textinputObject->text());
1434     layout.setFont(textinputObject->font());
1435
1436     if (!qmlDisableDistanceField()) {
1437         QTextOption option;
1438         option.setUseDesignMetrics(true);
1439         layout.setTextOption(option);
1440     }
1441     layout.beginLayout();
1442     QTextLine line = layout.createLine();
1443     layout.endLayout();
1444
1445     int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1446     int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1447     int textWidth = floor(line.horizontalAdvance());
1448
1449     QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1450     QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1451
1452     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1453     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1454     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1455
1456     // Check without autoscroll...
1457     textinputObject->setAutoScroll(false);
1458     pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1459
1460     textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1461     textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1462
1463     QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1464     QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1465
1466     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1467     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1468     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1469
1470     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1471     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1472
1473     QString preeditText = textinputObject->text().mid(0, pos);
1474     textinputObject->setText(textinputObject->text().mid(pos));
1475     textinputObject->setCursorPosition(0);
1476
1477     {   QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1478         QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1479
1480     // Check all points within the preedit text return the same position.
1481     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1482     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1483     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1484
1485     // Verify positioning returns to normal after the preedit text.
1486     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1487     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1488
1489     {   QInputMethodEvent inputEvent;
1490         QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1491
1492     // With wrapping.
1493     textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1494
1495     const qreal y0 = line.height() / 2;
1496     const qreal y1 = line.height() * 3 / 2;
1497
1498     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1499     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1500
1501     int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1502     QVERIFY(newLinePos > pos);
1503     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1504 }
1505
1506 void tst_qquicktextinput::maxLength()
1507 {
1508     QQuickView canvas(testFileUrl("maxLength.qml"));
1509     QVERIFY(canvas.rootObject() != 0);
1510     canvas.show();
1511     canvas.requestActivateWindow();
1512     QTest::qWaitForWindowShown(&canvas);
1513
1514     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1515     QVERIFY(textinputObject != 0);
1516     QVERIFY(textinputObject->text().isEmpty());
1517     QVERIFY(textinputObject->maxLength() == 10);
1518     foreach (const QString &str, standard) {
1519         QVERIFY(textinputObject->text().length() <= 10);
1520         textinputObject->setText(str);
1521         QVERIFY(textinputObject->text().length() <= 10);
1522     }
1523
1524     textinputObject->setText("");
1525     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1526     for (int i=0; i<20; i++) {
1527         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1528         //simulateKey(&canvas, Qt::Key_A);
1529         QTest::keyPress(&canvas, Qt::Key_A);
1530         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1531         QTest::qWait(50);
1532     }
1533 }
1534
1535 void tst_qquicktextinput::masks()
1536 {
1537     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1538     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1539     QQuickView canvas(testFileUrl("masks.qml"));
1540     canvas.show();
1541     canvas.requestActivateWindow();
1542     QVERIFY(canvas.rootObject() != 0);
1543     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1544     QVERIFY(textinputObject != 0);
1545     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1546     QVERIFY(textinputObject->text().length() == 0);
1547     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1548     QCOMPARE(textinputObject->length(), 8);
1549     for (int i=0; i<10; i++) {
1550         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1551         QCOMPARE(textinputObject->length(), 8);
1552         QCOMPARE(textinputObject->getText(0, qMin(i, 8)), QString(qMin(i, 8), 'a'));
1553         QCOMPARE(textinputObject->getText(qMin(i, 8), 8), QString(8 - qMin(i, 8), ' '));
1554         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1555         //simulateKey(&canvas, Qt::Key_A);
1556         QTest::keyPress(&canvas, Qt::Key_A);
1557         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1558         QTest::qWait(50);
1559     }
1560 }
1561
1562 void tst_qquicktextinput::validators()
1563 {
1564     // Note that this test assumes that the validators are working properly
1565     // so you may need to run their tests first. All validators are checked
1566     // here to ensure that their exposure to QML is working.
1567
1568     QLocale::setDefault(QLocale(QStringLiteral("C")));
1569
1570     QQuickView canvas(testFileUrl("validators.qml"));
1571     canvas.show();
1572     canvas.requestActivateWindow();
1573
1574     QVERIFY(canvas.rootObject() != 0);
1575
1576     QLocale defaultLocale;
1577     QLocale enLocale("en");
1578     QLocale deLocale("de_DE");
1579
1580     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1581     QVERIFY(intInput);
1582     QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
1583
1584     QQuickIntValidator *intValidator = qobject_cast<QQuickIntValidator *>(intInput->validator());
1585     QVERIFY(intValidator);
1586     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1587     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1588     intValidator->setLocaleName(enLocale.name());
1589     QCOMPARE(intValidator->localeName(), enLocale.name());
1590     QCOMPARE(intInput->validator()->locale(), enLocale);
1591     intValidator->resetLocaleName();
1592     QCOMPARE(intValidator->localeName(), defaultLocale.name());
1593     QCOMPARE(intInput->validator()->locale(), defaultLocale);
1594
1595     intInput->setFocus(true);
1596     QTRY_VERIFY(intInput->hasActiveFocus());
1597     QCOMPARE(intInput->hasAcceptableInput(), false);
1598     QCOMPARE(intInput->property("acceptable").toBool(), false);
1599     QTest::keyPress(&canvas, Qt::Key_1);
1600     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1601     QTest::qWait(50);
1602     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1603     QCOMPARE(intInput->hasAcceptableInput(), false);
1604     QCOMPARE(intInput->property("acceptable").toBool(), false);
1605     QCOMPARE(intSpy.count(), 0);
1606     QTest::keyPress(&canvas, Qt::Key_2);
1607     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1608     QTest::qWait(50);
1609     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1610     QCOMPARE(intInput->hasAcceptableInput(), false);
1611     QCOMPARE(intInput->property("acceptable").toBool(), false);
1612     QCOMPARE(intSpy.count(), 0);
1613     QTest::keyPress(&canvas, Qt::Key_Period);
1614     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1615     QTest::qWait(50);
1616     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1617     QCOMPARE(intInput->hasAcceptableInput(), false);
1618     QTest::keyPress(&canvas, Qt::Key_Comma);
1619     QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1620     QTest::qWait(50);
1621     QTRY_COMPARE(intInput->text(), QLatin1String("1,"));
1622     QCOMPARE(intInput->hasAcceptableInput(), false);
1623     QTest::keyPress(&canvas, Qt::Key_Backspace);
1624     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1625     QTest::qWait(50);
1626     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1627     QCOMPARE(intInput->hasAcceptableInput(), false);
1628     intValidator->setLocaleName(deLocale.name());
1629     QTest::keyPress(&canvas, Qt::Key_Period);
1630     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1631     QTest::qWait(50);
1632     QTRY_COMPARE(intInput->text(), QLatin1String("1."));
1633     QCOMPARE(intInput->hasAcceptableInput(), false);
1634     QTest::keyPress(&canvas, Qt::Key_Backspace);
1635     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1636     QTest::qWait(50);
1637     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1638     QCOMPARE(intInput->hasAcceptableInput(), false);
1639     intValidator->resetLocaleName();
1640     QTest::keyPress(&canvas, Qt::Key_1);
1641     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1642     QTest::qWait(50);
1643     QCOMPARE(intInput->text(), QLatin1String("11"));
1644     QCOMPARE(intInput->hasAcceptableInput(), true);
1645     QCOMPARE(intInput->property("acceptable").toBool(), true);
1646     QCOMPARE(intSpy.count(), 1);
1647     QTest::keyPress(&canvas, Qt::Key_0);
1648     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1649     QTest::qWait(50);
1650     QCOMPARE(intInput->text(), QLatin1String("11"));
1651     QCOMPARE(intInput->hasAcceptableInput(), true);
1652     QCOMPARE(intInput->property("acceptable").toBool(), true);
1653     QCOMPARE(intSpy.count(), 1);
1654
1655     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1656     QVERIFY(dblInput);
1657     QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
1658
1659     QQuickDoubleValidator *dblValidator = qobject_cast<QQuickDoubleValidator *>(dblInput->validator());
1660     QVERIFY(dblValidator);
1661     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1662     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1663     dblValidator->setLocaleName(enLocale.name());
1664     QCOMPARE(dblValidator->localeName(), enLocale.name());
1665     QCOMPARE(dblInput->validator()->locale(), enLocale);
1666     dblValidator->resetLocaleName();
1667     QCOMPARE(dblValidator->localeName(), defaultLocale.name());
1668     QCOMPARE(dblInput->validator()->locale(), defaultLocale);
1669
1670     dblInput->setFocus(true);
1671     QVERIFY(dblInput->hasActiveFocus() == true);
1672     QCOMPARE(dblInput->hasAcceptableInput(), false);
1673     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1674     QTest::keyPress(&canvas, Qt::Key_1);
1675     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1676     QTest::qWait(50);
1677     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1678     QCOMPARE(dblInput->hasAcceptableInput(), false);
1679     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1680     QCOMPARE(dblSpy.count(), 0);
1681     QTest::keyPress(&canvas, Qt::Key_2);
1682     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1683     QTest::qWait(50);
1684     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1685     QCOMPARE(dblInput->hasAcceptableInput(), true);
1686     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1687     QCOMPARE(dblSpy.count(), 1);
1688     QTest::keyPress(&canvas, Qt::Key_Comma);
1689     QTest::keyRelease(&canvas, Qt::Key_Comma, Qt::NoModifier ,10);
1690     QTest::qWait(50);
1691     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1692     QCOMPARE(dblInput->hasAcceptableInput(), true);
1693     QTest::keyPress(&canvas, Qt::Key_1);
1694     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1695     QTest::qWait(50);
1696     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1697     QCOMPARE(dblInput->hasAcceptableInput(), true);
1698     dblValidator->setLocaleName(deLocale.name());
1699     QCOMPARE(dblInput->hasAcceptableInput(), true);
1700     QTest::keyPress(&canvas, Qt::Key_1);
1701     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1702     QTest::qWait(50);
1703     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1704     QCOMPARE(dblInput->hasAcceptableInput(), true);
1705     QTest::keyPress(&canvas, Qt::Key_1);
1706     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1707     QTest::qWait(50);
1708     QTRY_COMPARE(dblInput->text(), QLatin1String("12,11"));
1709     QCOMPARE(dblInput->hasAcceptableInput(), true);
1710     QTest::keyPress(&canvas, Qt::Key_Backspace);
1711     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1712     QTest::qWait(50);
1713     QTRY_COMPARE(dblInput->text(), QLatin1String("12,1"));
1714     QCOMPARE(dblInput->hasAcceptableInput(), true);
1715     QTest::keyPress(&canvas, Qt::Key_Backspace);
1716     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1717     QTest::qWait(50);
1718     QTRY_COMPARE(dblInput->text(), QLatin1String("12,"));
1719     QCOMPARE(dblInput->hasAcceptableInput(), true);
1720     QTest::keyPress(&canvas, Qt::Key_Backspace);
1721     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1722     QTest::qWait(50);
1723     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1724     QCOMPARE(dblInput->hasAcceptableInput(), true);
1725     dblValidator->resetLocaleName();
1726     QTest::keyPress(&canvas, Qt::Key_Period);
1727     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1728     QTest::qWait(50);
1729     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1730     QCOMPARE(dblInput->hasAcceptableInput(), true);
1731     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1732     QCOMPARE(dblSpy.count(), 1);
1733     QTest::keyPress(&canvas, Qt::Key_1);
1734     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1735     QTest::qWait(50);
1736     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1737     QCOMPARE(dblInput->hasAcceptableInput(), true);
1738     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1739     QCOMPARE(dblSpy.count(), 1);
1740     QTest::keyPress(&canvas, Qt::Key_1);
1741     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1742     QTest::qWait(50);
1743     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1744     QCOMPARE(dblInput->hasAcceptableInput(), true);
1745     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1746     QCOMPARE(dblSpy.count(), 1);
1747     QTest::keyPress(&canvas, Qt::Key_1);
1748     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1749     QTest::qWait(50);
1750     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1751     QCOMPARE(dblInput->hasAcceptableInput(), true);
1752     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1753     QCOMPARE(dblSpy.count(), 1);
1754
1755     // Ensure the validator doesn't prevent characters being removed.
1756     dblInput->setValidator(intInput->validator());
1757     QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1758     QCOMPARE(dblInput->hasAcceptableInput(), false);
1759     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1760     QCOMPARE(dblSpy.count(), 2);
1761     QTest::keyPress(&canvas, Qt::Key_Backspace);
1762     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1763     QTest::qWait(50);
1764     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1765     QCOMPARE(dblInput->hasAcceptableInput(), false);
1766     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1767     QCOMPARE(dblSpy.count(), 2);
1768     // Once unacceptable input is in anything goes until it reaches an acceptable state again.
1769     QTest::keyPress(&canvas, Qt::Key_1);
1770     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1771     QTest::qWait(50);
1772     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1773     QCOMPARE(dblInput->hasAcceptableInput(), false);
1774     QCOMPARE(dblSpy.count(), 2);
1775     QTest::keyPress(&canvas, Qt::Key_Backspace);
1776     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1777     QTest::qWait(50);
1778     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1779     QCOMPARE(dblInput->hasAcceptableInput(), false);
1780     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1781     QCOMPARE(dblSpy.count(), 2);
1782     QTest::keyPress(&canvas, Qt::Key_Backspace);
1783     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1784     QTest::qWait(50);
1785     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1786     QCOMPARE(dblInput->hasAcceptableInput(), false);
1787     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1788     QCOMPARE(dblSpy.count(), 2);
1789     QTest::keyPress(&canvas, Qt::Key_Backspace);
1790     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1791     QTest::qWait(50);
1792     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1793     QCOMPARE(dblInput->hasAcceptableInput(), false);
1794     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1795     QCOMPARE(dblSpy.count(), 2);
1796     QTest::keyPress(&canvas, Qt::Key_Backspace);
1797     QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
1798     QTest::qWait(50);
1799     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1800     QCOMPARE(dblInput->hasAcceptableInput(), false);
1801     QCOMPARE(dblInput->property("acceptable").toBool(), false);
1802     QCOMPARE(dblSpy.count(), 2);
1803     QTest::keyPress(&canvas, Qt::Key_1);
1804     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1805     QTest::qWait(50);
1806     QCOMPARE(dblInput->text(), QLatin1String("11"));
1807     QCOMPARE(dblInput->property("acceptable").toBool(), true);
1808     QCOMPARE(dblInput->hasAcceptableInput(), true);
1809     QCOMPARE(dblSpy.count(), 3);
1810
1811     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1812     QVERIFY(strInput);
1813     QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
1814     strInput->setFocus(true);
1815     QVERIFY(strInput->hasActiveFocus() == true);
1816     QCOMPARE(strInput->hasAcceptableInput(), false);
1817     QCOMPARE(strInput->property("acceptable").toBool(), false);
1818     QTest::keyPress(&canvas, Qt::Key_1);
1819     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1820     QTest::qWait(50);
1821     QTRY_COMPARE(strInput->text(), QLatin1String(""));
1822     QCOMPARE(strInput->hasAcceptableInput(), false);
1823     QCOMPARE(strInput->property("acceptable").toBool(), false);
1824     QCOMPARE(strSpy.count(), 0);
1825     QTest::keyPress(&canvas, Qt::Key_A);
1826     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1827     QTest::qWait(50);
1828     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1829     QCOMPARE(strInput->hasAcceptableInput(), false);
1830     QCOMPARE(strInput->property("acceptable").toBool(), false);
1831     QCOMPARE(strSpy.count(), 0);
1832     QTest::keyPress(&canvas, Qt::Key_A);
1833     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1834     QTest::qWait(50);
1835     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1836     QCOMPARE(strInput->hasAcceptableInput(), true);
1837     QCOMPARE(strInput->property("acceptable").toBool(), true);
1838     QCOMPARE(strSpy.count(), 1);
1839     QTest::keyPress(&canvas, Qt::Key_A);
1840     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1841     QTest::qWait(50);
1842     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1843     QCOMPARE(strInput->hasAcceptableInput(), true);
1844     QCOMPARE(strInput->property("acceptable").toBool(), true);
1845     QCOMPARE(strSpy.count(), 1);
1846     QTest::keyPress(&canvas, Qt::Key_A);
1847     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1848     QTest::qWait(50);
1849     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1850     QCOMPARE(strInput->hasAcceptableInput(), true);
1851     QCOMPARE(strInput->property("acceptable").toBool(), true);
1852     QCOMPARE(strSpy.count(), 1);
1853     QTest::keyPress(&canvas, Qt::Key_A);
1854     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1855     QTest::qWait(50);
1856     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1857     QCOMPARE(strInput->hasAcceptableInput(), true);
1858     QCOMPARE(strInput->property("acceptable").toBool(), true);
1859     QCOMPARE(strSpy.count(), 1);
1860
1861     QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
1862     QVERIFY(unvalidatedInput);
1863     QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
1864     unvalidatedInput->setFocus(true);
1865     QVERIFY(unvalidatedInput->hasActiveFocus() == true);
1866     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1867     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1868     QTest::keyPress(&canvas, Qt::Key_1);
1869     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1870     QTest::qWait(50);
1871     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
1872     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1873     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1874     QCOMPARE(unvalidatedSpy.count(), 0);
1875     QTest::keyPress(&canvas, Qt::Key_A);
1876     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1877     QTest::qWait(50);
1878     QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
1879     QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
1880     QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
1881     QCOMPARE(unvalidatedSpy.count(), 0);
1882 }
1883
1884 void tst_qquicktextinput::inputMethods()
1885 {
1886     QQuickView canvas(testFileUrl("inputmethods.qml"));
1887     canvas.show();
1888     canvas.requestActivateWindow();
1889     QTest::qWaitForWindowShown(&canvas);
1890
1891     // test input method hints
1892     QVERIFY(canvas.rootObject() != 0);
1893     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1894     QVERIFY(input != 0);
1895     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1896     input->setInputMethodHints(Qt::ImhUppercaseOnly);
1897     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1898
1899     input->setFocus(true);
1900     QVERIFY(input->hasActiveFocus() == true);
1901     // test that input method event is committed
1902     QInputMethodEvent event;
1903     event.setCommitString( "My ", -12, 0);
1904     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1905     QCOMPARE(input->text(), QString("My Hello world!"));
1906
1907     input->setCursorPosition(2);
1908     event.setCommitString("Your", -2, 2);
1909     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1910     QCOMPARE(input->text(), QString("Your Hello world!"));
1911     QCOMPARE(input->cursorPosition(), 4);
1912
1913     input->setCursorPosition(7);
1914     event.setCommitString("Goodbye", -2, 5);
1915     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1916     QCOMPARE(input->text(), QString("Your Goodbye world!"));
1917     QCOMPARE(input->cursorPosition(), 12);
1918
1919     input->setCursorPosition(8);
1920     event.setCommitString("Our", -8, 4);
1921     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1922     QCOMPARE(input->text(), QString("Our Goodbye world!"));
1923     QCOMPARE(input->cursorPosition(), 7);
1924
1925     // test that basic tentative commit gets to text property on preedit state
1926     input->setText("");
1927     QList<QInputMethodEvent::Attribute> attributes;
1928     QInputMethodEvent preeditEvent("test", attributes);
1929     preeditEvent.setTentativeCommitString("test");
1930     QGuiApplication::sendEvent(input, &preeditEvent);
1931     QCOMPARE(input->text(), QString("test"));
1932
1933     // tentative commit not allowed present in surrounding text
1934     QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
1935     QGuiApplication::sendEvent(input, &queryEvent);
1936     QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
1937
1938     // if text with tentative commit does not validate, not allowed to be part of text property
1939     input->setText(""); // ensure input state is reset
1940     QValidator *validator = new QIntValidator(0, 100);
1941     input->setValidator(validator);
1942     QGuiApplication::sendEvent(input, &preeditEvent);
1943     QCOMPARE(input->text(), QString(""));
1944     input->setValidator(0);
1945     delete validator;
1946
1947     // input should reset selection even if replacement parameters are out of bounds
1948     input->setText("text");
1949     input->setCursorPosition(0);
1950     input->moveCursorSelection(input->text().length());
1951     event.setCommitString("replacement", -input->text().length(), input->text().length());
1952     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1953     QCOMPARE(input->selectionStart(), input->selectionEnd());
1954 }
1955
1956 /*
1957 TextInput element should only handle left/right keys until the cursor reaches
1958 the extent of the text, then they should ignore the keys.
1959
1960 */
1961 void tst_qquicktextinput::navigation()
1962 {
1963     QQuickView canvas(testFileUrl("navigation.qml"));
1964     canvas.show();
1965     canvas.requestActivateWindow();
1966
1967     QVERIFY(canvas.rootObject() != 0);
1968
1969     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1970
1971     QVERIFY(input != 0);
1972     input->setCursorPosition(0);
1973     QTRY_VERIFY(input->hasActiveFocus() == true);
1974     simulateKey(&canvas, Qt::Key_Left);
1975     QVERIFY(input->hasActiveFocus() == false);
1976     simulateKey(&canvas, Qt::Key_Right);
1977     QVERIFY(input->hasActiveFocus() == true);
1978     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1979     input->setCursorPosition(input->text().length());
1980     input->select(0,input->text().length());
1981     QVERIFY(input->selectionStart() != input->selectionEnd());
1982     simulateKey(&canvas, Qt::Key_Right);
1983     QVERIFY(input->selectionStart() == input->selectionEnd());
1984     QVERIFY(input->selectionStart() == input->text().length());
1985     QVERIFY(input->hasActiveFocus() == true);
1986     simulateKey(&canvas, Qt::Key_Right);
1987     QVERIFY(input->hasActiveFocus() == false);
1988     simulateKey(&canvas, Qt::Key_Left);
1989     QVERIFY(input->hasActiveFocus() == true);
1990
1991     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1992     input->setCursorPosition(2);
1993     QCOMPARE(input->cursorPosition(),2);
1994     simulateKey(&canvas, Qt::Key_Up);
1995     QCOMPARE(input->cursorPosition(),2);
1996     simulateKey(&canvas, Qt::Key_Down);
1997     QCOMPARE(input->cursorPosition(),2);
1998 }
1999
2000 void tst_qquicktextinput::navigation_RTL()
2001 {
2002     QQuickView canvas(testFileUrl("navigation.qml"));
2003     canvas.show();
2004     canvas.requestActivateWindow();
2005
2006     QVERIFY(canvas.rootObject() != 0);
2007
2008     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2009
2010     QVERIFY(input != 0);
2011     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2012     input->setText(QString::fromUtf16(arabic_str, 11));
2013
2014     input->setCursorPosition(0);
2015     QTRY_VERIFY(input->hasActiveFocus() == true);
2016
2017     // move off
2018     simulateKey(&canvas, Qt::Key_Right);
2019     QVERIFY(input->hasActiveFocus() == false);
2020
2021     // move back
2022     simulateKey(&canvas, Qt::Key_Left);
2023     QVERIFY(input->hasActiveFocus() == true);
2024
2025     input->setCursorPosition(input->text().length());
2026     QVERIFY(input->hasActiveFocus() == true);
2027
2028     // move off
2029     simulateKey(&canvas, Qt::Key_Left);
2030     QVERIFY(input->hasActiveFocus() == false);
2031
2032     // move back
2033     simulateKey(&canvas, Qt::Key_Right);
2034     QVERIFY(input->hasActiveFocus() == true);
2035 }
2036
2037 void tst_qquicktextinput::copyAndPaste() {
2038 #ifndef QT_NO_CLIPBOARD
2039
2040 #ifdef Q_OS_MAC
2041     {
2042         PasteboardRef pasteboard;
2043         OSStatus status = PasteboardCreate(0, &pasteboard);
2044         if (status == noErr)
2045             CFRelease(pasteboard);
2046         else
2047             QSKIP("This machine doesn't support the clipboard");
2048     }
2049 #endif
2050
2051     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2052     QDeclarativeComponent textInputComponent(&engine);
2053     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2054     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2055     QVERIFY(textInput != 0);
2056
2057     // copy and paste
2058     QCOMPARE(textInput->text().length(), 12);
2059     textInput->select(0, textInput->text().length());;
2060     textInput->copy();
2061     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2062     QCOMPARE(textInput->selectedText().length(), 12);
2063     textInput->setCursorPosition(0);
2064     QVERIFY(textInput->canPaste());
2065     textInput->paste();
2066     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2067     QCOMPARE(textInput->text().length(), 24);
2068
2069     // can paste
2070     QVERIFY(textInput->canPaste());
2071     textInput->setReadOnly(true);
2072     QVERIFY(!textInput->canPaste());
2073     textInput->setReadOnly(false);
2074     QVERIFY(textInput->canPaste());
2075
2076     // select word
2077     textInput->setCursorPosition(0);
2078     textInput->selectWord();
2079     QCOMPARE(textInput->selectedText(), QString("Hello"));
2080
2081     // select all and cut
2082     textInput->selectAll();
2083     textInput->cut();
2084     QCOMPARE(textInput->text().length(), 0);
2085     textInput->paste();
2086     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2087     QCOMPARE(textInput->text().length(), 24);
2088
2089     // clear copy buffer
2090     QClipboard *clipboard = QGuiApplication::clipboard();
2091     QVERIFY(clipboard);
2092     clipboard->clear();
2093     QVERIFY(!textInput->canPaste());
2094
2095     // test that copy functionality is disabled
2096     // when echo mode is set to hide text/password mode
2097     int index = 0;
2098     while (index < 4) {
2099         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2100         textInput->setEchoMode(echoMode);
2101         textInput->setText("My password");
2102         textInput->select(0, textInput->text().length());;
2103         textInput->copy();
2104         if (echoMode == QQuickTextInput::Normal) {
2105             QVERIFY(!clipboard->text().isEmpty());
2106             QCOMPARE(clipboard->text(), QString("My password"));
2107             clipboard->clear();
2108         } else {
2109             QVERIFY(clipboard->text().isEmpty());
2110         }
2111         index++;
2112     }
2113
2114     delete textInput;
2115 #endif
2116 }
2117
2118 void tst_qquicktextinput::copyAndPasteKeySequence() {
2119 #ifndef QT_NO_CLIPBOARD
2120
2121 #ifdef Q_OS_MAC
2122     {
2123         PasteboardRef pasteboard;
2124         OSStatus status = PasteboardCreate(0, &pasteboard);
2125         if (status == noErr)
2126             CFRelease(pasteboard);
2127         else
2128             QSKIP("This machine doesn't support the clipboard");
2129     }
2130 #endif
2131
2132     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2133     QDeclarativeComponent textInputComponent(&engine);
2134     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2135     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2136     QVERIFY(textInput != 0);
2137
2138     QQuickCanvas canvas;
2139     textInput->setParentItem(canvas.rootItem());
2140     canvas.show();
2141     canvas.requestActivateWindow();
2142     QTest::qWaitForWindowShown(&canvas);
2143     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2144
2145     // copy and paste
2146     QVERIFY(textInput->hasActiveFocus());
2147     QCOMPARE(textInput->text().length(), 12);
2148     textInput->select(0, textInput->text().length());
2149     simulateKeys(&canvas, QKeySequence::Copy);
2150     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2151     QCOMPARE(textInput->selectedText().length(), 12);
2152     textInput->setCursorPosition(0);
2153     QVERIFY(textInput->canPaste());
2154     simulateKeys(&canvas, QKeySequence::Paste);
2155     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2156     QCOMPARE(textInput->text().length(), 24);
2157
2158     // select all and cut
2159     simulateKeys(&canvas, QKeySequence::SelectAll);
2160     simulateKeys(&canvas, QKeySequence::Cut);
2161     QCOMPARE(textInput->text().length(), 0);
2162     simulateKeys(&canvas, QKeySequence::Paste);
2163     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2164     QCOMPARE(textInput->text().length(), 24);
2165
2166     // clear copy buffer
2167     QClipboard *clipboard = QGuiApplication::clipboard();
2168     QVERIFY(clipboard);
2169     clipboard->clear();
2170     QVERIFY(!textInput->canPaste());
2171
2172     // test that copy functionality is disabled
2173     // when echo mode is set to hide text/password mode
2174     int index = 0;
2175     while (index < 4) {
2176         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2177         textInput->setEchoMode(echoMode);
2178         textInput->setText("My password");
2179         textInput->select(0, textInput->text().length());;
2180         simulateKeys(&canvas, QKeySequence::Copy);
2181         if (echoMode == QQuickTextInput::Normal) {
2182             QVERIFY(!clipboard->text().isEmpty());
2183             QCOMPARE(clipboard->text(), QString("My password"));
2184             clipboard->clear();
2185         } else {
2186             QVERIFY(clipboard->text().isEmpty());
2187         }
2188         index++;
2189     }
2190
2191     delete textInput;
2192 #endif
2193 }
2194
2195 void tst_qquicktextinput::canPasteEmpty() {
2196 #ifndef QT_NO_CLIPBOARD
2197
2198     QGuiApplication::clipboard()->clear();
2199
2200     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2201     QDeclarativeComponent textInputComponent(&engine);
2202     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2203     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2204     QVERIFY(textInput != 0);
2205
2206     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2207     QCOMPARE(textInput->canPaste(), cp);
2208
2209 #endif
2210 }
2211
2212 void tst_qquicktextinput::canPaste() {
2213 #ifndef QT_NO_CLIPBOARD
2214
2215     QGuiApplication::clipboard()->setText("Some text");
2216
2217     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2218     QDeclarativeComponent textInputComponent(&engine);
2219     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2220     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2221     QVERIFY(textInput != 0);
2222
2223     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2224     QCOMPARE(textInput->canPaste(), cp);
2225
2226 #endif
2227 }
2228
2229 void tst_qquicktextinput::passwordCharacter()
2230 {
2231     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2232     QDeclarativeComponent textInputComponent(&engine);
2233     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2234     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2235     QVERIFY(textInput != 0);
2236
2237     textInput->setPasswordCharacter("X");
2238     qreal implicitWidth = textInput->implicitWidth();
2239     textInput->setPasswordCharacter(".");
2240
2241     // QTBUG-12383 content is updated and redrawn
2242     QVERIFY(textInput->implicitWidth() < implicitWidth);
2243
2244     delete textInput;
2245 }
2246
2247 void tst_qquicktextinput::cursorDelegate()
2248 {
2249     QQuickView view(testFileUrl("cursorTest.qml"));
2250     view.show();
2251     view.requestActivateWindow();
2252     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2253     QVERIFY(textInputObject != 0);
2254     QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2255     //Test Delegate gets created
2256     textInputObject->setFocus(true);
2257     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2258     QVERIFY(delegateObject);
2259     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2260     //Test Delegate gets moved
2261     for (int i=0; i<= textInputObject->text().length(); i++) {
2262         textInputObject->setCursorPosition(i);
2263         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2264         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2265     }
2266     textInputObject->setCursorPosition(0);
2267     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2268     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2269     //Test Delegate gets deleted
2270     textInputObject->setCursorDelegate(0);
2271     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2272 }
2273
2274 void tst_qquicktextinput::cursorVisible()
2275 {
2276     QQuickView view(testFileUrl("cursorVisible.qml"));
2277     view.show();
2278     view.requestActivateWindow();
2279     QTest::qWaitForWindowShown(&view);
2280     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2281
2282     QQuickTextInput input;
2283     input.componentComplete();
2284     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2285
2286     QCOMPARE(input.isCursorVisible(), false);
2287
2288     input.setCursorVisible(true);
2289     QCOMPARE(input.isCursorVisible(), true);
2290     QCOMPARE(spy.count(), 1);
2291
2292     input.setCursorVisible(false);
2293     QCOMPARE(input.isCursorVisible(), false);
2294     QCOMPARE(spy.count(), 2);
2295
2296     input.setFocus(true);
2297     QCOMPARE(input.isCursorVisible(), false);
2298     QCOMPARE(spy.count(), 2);
2299
2300     input.setParentItem(view.rootObject());
2301     QCOMPARE(input.isCursorVisible(), true);
2302     QCOMPARE(spy.count(), 3);
2303
2304     input.setFocus(false);
2305     QCOMPARE(input.isCursorVisible(), false);
2306     QCOMPARE(spy.count(), 4);
2307
2308     input.setFocus(true);
2309     QCOMPARE(input.isCursorVisible(), true);
2310     QCOMPARE(spy.count(), 5);
2311
2312     QQuickView alternateView;
2313     alternateView.show();
2314     alternateView.requestActivateWindow();
2315     QTest::qWaitForWindowShown(&alternateView);
2316
2317     QCOMPARE(input.isCursorVisible(), false);
2318     QCOMPARE(spy.count(), 6);
2319
2320     view.requestActivateWindow();
2321     QTest::qWaitForWindowShown(&view);
2322     QCOMPARE(input.isCursorVisible(), true);
2323     QCOMPARE(spy.count(), 7);
2324 }
2325
2326 void tst_qquicktextinput::cursorRectangle()
2327 {
2328
2329     QString text = "Hello World!";
2330
2331     QQuickTextInput input;
2332     input.setText(text);
2333     input.componentComplete();
2334
2335     QTextLayout layout(text);
2336     layout.setFont(input.font());
2337     if (!qmlDisableDistanceField()) {
2338         QTextOption option;
2339         option.setUseDesignMetrics(true);
2340         layout.setTextOption(option);
2341     }
2342     layout.beginLayout();
2343     QTextLine line = layout.createLine();
2344     layout.endLayout();
2345
2346     input.setWidth(line.cursorToX(5, QTextLine::Leading));
2347     input.setHeight(qCeil(line.height() * 3 / 2));
2348
2349     QRect r;
2350
2351     // some tolerance for different fonts.
2352 #ifdef Q_OS_LINUX
2353     const int error = 2;
2354 #else
2355     const int error = 5;
2356 #endif
2357
2358     for (int i = 0; i <= 5; ++i) {
2359         input.setCursorPosition(i);
2360         r = input.cursorRectangle();
2361
2362         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2363         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2364         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2365     }
2366
2367     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2368     QVERIFY(r.left() < input.width());
2369     QVERIFY(r.right() >= input.width() - error);
2370
2371     for (int i = 6; i < text.length(); ++i) {
2372         input.setCursorPosition(i);
2373         QCOMPARE(r, input.cursorRectangle());
2374         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2375     }
2376
2377     for (int i = text.length() - 2; i >= 0; --i) {
2378         input.setCursorPosition(i);
2379         r = input.cursorRectangle();
2380         QCOMPARE(r.top(), 0);
2381         QVERIFY(r.right() >= 0);
2382         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2383     }
2384
2385     // Check vertical scrolling with word wrap.
2386     input.setWrapMode(QQuickTextInput::WordWrap);
2387     for (int i = 0; i <= 5; ++i) {
2388         input.setCursorPosition(i);
2389         r = input.cursorRectangle();
2390
2391         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2392         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2393         QCOMPARE(r.top(), 0);
2394         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2395     }
2396
2397     input.setCursorPosition(6);
2398     r = input.cursorRectangle();
2399     QCOMPARE(r.left(), 0);
2400     QVERIFY(r.bottom() >= input.height() - error);
2401
2402     for (int i = 7; i < text.length(); ++i) {
2403         input.setCursorPosition(i);
2404         r = input.cursorRectangle();
2405         QVERIFY(r.bottom() >= input.height() - error);
2406     }
2407
2408     for (int i = text.length() - 2; i >= 6; --i) {
2409         input.setCursorPosition(i);
2410         r = input.cursorRectangle();
2411         QVERIFY(r.bottom() >= input.height() - error);
2412     }
2413
2414     for (int i = 5; i >= 0; --i) {
2415         input.setCursorPosition(i);
2416         r = input.cursorRectangle();
2417         QCOMPARE(r.top(), 0);
2418     }
2419
2420     input.setText("Hi!");
2421     input.setHAlign(QQuickTextInput::AlignRight);
2422     r = input.cursorRectangle();
2423     QVERIFY(r.left() < input.width() + error);
2424     QVERIFY(r.right() >= input.width() - error);
2425 }
2426
2427 void tst_qquicktextinput::readOnly()
2428 {
2429     QQuickView canvas(testFileUrl("readOnly.qml"));
2430     canvas.show();
2431     canvas.requestActivateWindow();
2432
2433     QVERIFY(canvas.rootObject() != 0);
2434
2435     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2436
2437     QVERIFY(input != 0);
2438     QTRY_VERIFY(input->hasActiveFocus() == true);
2439     QVERIFY(input->isReadOnly() == true);
2440     QString initial = input->text();
2441     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2442         simulateKey(&canvas, k);
2443     simulateKey(&canvas, Qt::Key_Return);
2444     simulateKey(&canvas, Qt::Key_Space);
2445     simulateKey(&canvas, Qt::Key_Escape);
2446     QCOMPARE(input->text(), initial);
2447
2448     input->setCursorPosition(3);
2449     input->setReadOnly(false);
2450     QCOMPARE(input->isReadOnly(), false);
2451     QCOMPARE(input->cursorPosition(), input->text().length());
2452 }
2453
2454 void tst_qquicktextinput::echoMode()
2455 {
2456     QQuickView canvas(testFileUrl("echoMode.qml"));
2457     canvas.show();
2458     canvas.requestActivateWindow();
2459     QTest::qWaitForWindowShown(&canvas);
2460     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2461
2462     QVERIFY(canvas.rootObject() != 0);
2463
2464     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2465
2466     QVERIFY(input != 0);
2467     QTRY_VERIFY(input->hasActiveFocus() == true);
2468     QString initial = input->text();
2469     Qt::InputMethodHints ref;
2470     QCOMPARE(initial, QLatin1String("ABCDefgh"));
2471     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2472     QCOMPARE(input->displayText(), input->text());
2473     //Normal
2474     ref &= ~Qt::ImhHiddenText;
2475     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2476     QCOMPARE(input->inputMethodHints(), ref);
2477     input->setEchoMode(QQuickTextInput::NoEcho);
2478     QCOMPARE(input->text(), initial);
2479     QCOMPARE(input->displayText(), QLatin1String(""));
2480     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2481     //NoEcho
2482     ref |= Qt::ImhHiddenText;
2483     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2484     QCOMPARE(input->inputMethodHints(), ref);
2485     input->setEchoMode(QQuickTextInput::Password);
2486     //Password
2487     ref |= Qt::ImhHiddenText;
2488     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2489     QCOMPARE(input->text(), initial);
2490     QCOMPARE(input->displayText(), QLatin1String("********"));
2491     QCOMPARE(input->inputMethodHints(), ref);
2492     input->setPasswordCharacter(QChar('Q'));
2493     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2494     QCOMPARE(input->text(), initial);
2495     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2496     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2497     //PasswordEchoOnEdit
2498     ref &= ~Qt::ImhHiddenText;
2499     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2500     QCOMPARE(input->inputMethodHints(), ref);
2501     QCOMPARE(input->text(), initial);
2502     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2503     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2504     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2505     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2506     QCOMPARE(input->text(), QLatin1String("a"));
2507     QCOMPARE(input->displayText(), QLatin1String("a"));
2508     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2509     input->setFocus(false);
2510     QVERIFY(input->hasActiveFocus() == false);
2511     QCOMPARE(input->displayText(), QLatin1String("Q"));
2512     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2513     input->setFocus(true);
2514     QVERIFY(input->hasActiveFocus());
2515     QInputMethodEvent inputEvent;
2516     inputEvent.setCommitString(initial);
2517     QGuiApplication::sendEvent(input, &inputEvent);
2518     QCOMPARE(input->text(), initial);
2519     QCOMPARE(input->displayText(), initial);
2520     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2521 }
2522
2523 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2524 void tst_qquicktextinput::passwordEchoDelay()
2525 {
2526     QQuickView canvas(testFileUrl("echoMode.qml"));
2527     canvas.show();
2528     canvas.requestActivateWindow();
2529     QTest::qWaitForWindowShown(&canvas);
2530     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2531
2532     QVERIFY(canvas.rootObject() != 0);
2533
2534     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2535
2536     QChar fillChar = QLatin1Char('*');
2537
2538     input->setEchoMode(QQuickTextInput::Password);
2539     QCOMPARE(input->displayText(), QString(8, fillChar));
2540     input->setText(QString());
2541     QCOMPARE(input->displayText(), QString());
2542
2543     QTest::keyPress(&canvas, '0');
2544     QTest::keyPress(&canvas, '1');
2545     QTest::keyPress(&canvas, '2');
2546     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2547     QTest::keyPress(&canvas, '3');
2548     QTest::keyPress(&canvas, '4');
2549     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2550     QTest::keyPress(&canvas, Qt::Key_Backspace);
2551     QCOMPARE(input->displayText(), QString(4, fillChar));
2552     QTest::keyPress(&canvas, '4');
2553     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2554     QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2555     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2556     QTest::keyPress(&canvas, '5');
2557     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2558     input->setFocus(false);
2559     QVERIFY(!input->hasFocus());
2560     QCOMPARE(input->displayText(), QString(6, fillChar));
2561     input->setFocus(true);
2562     QTRY_VERIFY(input->hasFocus());
2563     QCOMPARE(input->displayText(), QString(6, fillChar));
2564     QTest::keyPress(&canvas, '6');
2565     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2566
2567     QInputMethodEvent ev;
2568     ev.setCommitString(QLatin1String("7"));
2569     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2570     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2571
2572     input->setCursorPosition(3);
2573     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2574     QTest::keyPress(&canvas, 'a');
2575     QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2576     QTest::keyPress(&canvas, Qt::Key_Backspace);
2577     QCOMPARE(input->displayText(), QString(8, fillChar));
2578 }
2579 #endif
2580
2581
2582 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2583 {
2584     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2585     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2586
2587     QGuiApplication::sendEvent(view, &press);
2588     QGuiApplication::sendEvent(view, &release);
2589 }
2590
2591
2592 void tst_qquicktextinput::openInputPanel()
2593 {
2594     PlatformInputContext platformInputContext;
2595     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2596     inputPanelPrivate->testContext = &platformInputContext;
2597
2598     QQuickView view(testFileUrl("openInputPanel.qml"));
2599     view.show();
2600     view.requestActivateWindow();
2601     QTest::qWaitForWindowShown(&view);
2602     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2603
2604     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2605     QVERIFY(input);
2606
2607     // check default values
2608     QVERIFY(input->focusOnPress());
2609     QVERIFY(!input->hasActiveFocus());
2610     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2611     QCOMPARE(qApp->inputPanel()->visible(), false);
2612
2613     // input panel should open on focus
2614     QPoint centerPoint(view.width()/2, view.height()/2);
2615     Qt::KeyboardModifiers noModifiers = 0;
2616     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2617     QGuiApplication::processEvents();
2618     QVERIFY(input->hasActiveFocus());
2619     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2620     QCOMPARE(qApp->inputPanel()->visible(), true);
2621     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2622
2623     // input panel should be re-opened when pressing already focused TextInput
2624     qApp->inputPanel()->hide();
2625     QCOMPARE(qApp->inputPanel()->visible(), false);
2626     QVERIFY(input->hasActiveFocus());
2627     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2628     QGuiApplication::processEvents();
2629     QCOMPARE(qApp->inputPanel()->visible(), true);
2630     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2631
2632     // input panel should stay visible if focus is lost to another text inputor
2633     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2634     QQuickTextInput anotherInput;
2635     anotherInput.componentComplete();
2636     anotherInput.setParentItem(view.rootObject());
2637     anotherInput.setFocus(true);
2638     QCOMPARE(qApp->inputPanel()->visible(), true);
2639     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2640     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2641
2642     anotherInput.setFocus(false);
2643     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2644     QCOMPARE(view.activeFocusItem(), view.rootItem());
2645     anotherInput.setFocus(true);
2646
2647     // input item should be null if focus is lost to an item that doesn't accept inputs
2648     QQuickItem item;
2649     item.setParentItem(view.rootObject());
2650     item.setFocus(true);
2651     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2652     QCOMPARE(view.activeFocusItem(), &item);
2653
2654     qApp->inputPanel()->hide();
2655
2656     // input panel should not be opened if TextInput is read only
2657     input->setReadOnly(true);
2658     input->setFocus(true);
2659     QCOMPARE(qApp->inputPanel()->visible(), false);
2660     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2661     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2662     QGuiApplication::processEvents();
2663     QCOMPARE(qApp->inputPanel()->visible(), false);
2664
2665     // input panel should not be opened if focusOnPress is set to false
2666     input->setFocusOnPress(false);
2667     input->setFocus(false);
2668     input->setFocus(true);
2669     QCOMPARE(qApp->inputPanel()->visible(), false);
2670     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2671     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2672     QCOMPARE(qApp->inputPanel()->visible(), false);
2673
2674     // input panel should open when openSoftwareInputPanel is called
2675     input->openSoftwareInputPanel();
2676     QCOMPARE(qApp->inputPanel()->visible(), true);
2677
2678     // input panel should close when closeSoftwareInputPanel is called
2679     input->closeSoftwareInputPanel();
2680     QCOMPARE(qApp->inputPanel()->visible(), false);
2681 }
2682
2683 class MyTextInput : public QQuickTextInput
2684 {
2685 public:
2686     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2687     {
2688         nbPaint = 0;
2689     }
2690     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2691     {
2692        nbPaint++;
2693        return QQuickTextInput::updatePaintNode(node, data);
2694     }
2695     int nbPaint;
2696 };
2697
2698 void tst_qquicktextinput::setHAlignClearCache()
2699 {
2700     QQuickView view;
2701     MyTextInput input;
2702     input.setText("Hello world");
2703     input.setParentItem(view.rootItem());
2704     view.show();
2705     view.requestActivateWindow();
2706     QTest::qWaitForWindowShown(&view);
2707 #ifdef Q_OS_MAC
2708     QEXPECT_FAIL("", "QTBUG-23485", Abort);
2709 #endif
2710     QTRY_COMPARE(input.nbPaint, 1);
2711     input.setHAlign(QQuickTextInput::AlignRight);
2712     //Changing the alignment should trigger a repaint
2713     QTRY_COMPARE(input.nbPaint, 2);
2714 }
2715
2716 void tst_qquicktextinput::focusOutClearSelection()
2717 {
2718     QQuickView view;
2719     QQuickTextInput input;
2720     QQuickTextInput input2;
2721     input.setText(QLatin1String("Hello world"));
2722     input.setFocus(true);
2723     input2.setParentItem(view.rootItem());
2724     input.setParentItem(view.rootItem());
2725     input.componentComplete();
2726     input2.componentComplete();
2727     view.show();
2728     view.requestActivateWindow();
2729     QTest::qWaitForWindowShown(&view);
2730     input.select(2,5);
2731     //The selection should work
2732     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2733     input2.setFocus(true);
2734     QGuiApplication::processEvents();
2735     //The input lost the focus selection should be cleared
2736     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2737 }
2738
2739 void tst_qquicktextinput::geometrySignals()
2740 {
2741     QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2742     QObject *o = component.create();
2743     QVERIFY(o);
2744     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2745     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2746     delete o;
2747 }
2748
2749 void tst_qquicktextinput::testQtQuick11Attributes()
2750 {
2751     QFETCH(QString, code);
2752     QFETCH(QString, warning);
2753     QFETCH(QString, error);
2754
2755     QDeclarativeEngine engine;
2756     QObject *obj;
2757
2758     QDeclarativeComponent valid(&engine);
2759     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2760     obj = valid.create();
2761     QVERIFY(obj);
2762     QVERIFY(valid.errorString().isEmpty());
2763     delete obj;
2764
2765     QDeclarativeComponent invalid(&engine);
2766     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2767     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2768     obj = invalid.create();
2769     QCOMPARE(invalid.errorString(), error);
2770     delete obj;
2771 }
2772
2773 void tst_qquicktextinput::testQtQuick11Attributes_data()
2774 {
2775     QTest::addColumn<QString>("code");
2776     QTest::addColumn<QString>("warning");
2777     QTest::addColumn<QString>("error");
2778
2779     QTest::newRow("canPaste") << "property bool foo: canPaste"
2780         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2781         << "";
2782
2783     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2784         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2785         << "";
2786
2787     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2788         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2789         << "";
2790 }
2791
2792 static void sendPreeditText(const QString &text, int cursor)
2793 {
2794     QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2795             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2796     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2797 }
2798
2799 void tst_qquicktextinput::preeditAutoScroll()
2800 {
2801     QString preeditText = "califragisiticexpialidocious!";
2802
2803     QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2804     view.show();
2805     view.requestActivateWindow();
2806     QTest::qWaitForWindowShown(&view);
2807     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2808     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2809     QVERIFY(input);
2810     QVERIFY(input->hasActiveFocus());
2811
2812     input->setWidth(input->implicitWidth());
2813
2814     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2815     int cursorRectangleChanges = 0;
2816
2817     // test the text is scrolled so the preedit is visible.
2818     sendPreeditText(preeditText.mid(0, 3), 1);
2819     QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2820     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2821     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2822
2823     // test the text is scrolled back when the preedit is removed.
2824     QInputMethodEvent imEvent;
2825     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2826     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2827     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2828     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2829
2830     QTextLayout layout(preeditText);
2831     layout.setFont(input->font());
2832     if (!qmlDisableDistanceField()) {
2833         QTextOption option;
2834         option.setUseDesignMetrics(true);
2835         layout.setTextOption(option);
2836     }
2837     layout.beginLayout();
2838     QTextLine line = layout.createLine();
2839     layout.endLayout();
2840
2841     // test if the preedit is larger than the text input that the
2842     // character preceding the cursor is still visible.
2843     qreal x = input->positionToRectangle(0).x();
2844     for (int i = 0; i < 3; ++i) {
2845         sendPreeditText(preeditText, i + 1);
2846         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2847         QVERIFY(input->cursorRectangle().right() >= width - 3);
2848         QVERIFY(input->positionToRectangle(0).x() < x);
2849         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2850         x = input->positionToRectangle(0).x();
2851     }
2852     for (int i = 1; i >= 0; --i) {
2853         sendPreeditText(preeditText, i + 1);
2854         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2855         QVERIFY(input->cursorRectangle().right() >= width - 3);
2856         QVERIFY(input->positionToRectangle(0).x() > x);
2857         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2858         x = input->positionToRectangle(0).x();
2859     }
2860
2861     // Test incrementing the preedit cursor doesn't cause further
2862     // scrolling when right most text is visible.
2863     sendPreeditText(preeditText, preeditText.length() - 3);
2864     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2865     x = input->positionToRectangle(0).x();
2866     for (int i = 2; i >= 0; --i) {
2867         sendPreeditText(preeditText, preeditText.length() - i);
2868         QCOMPARE(input->positionToRectangle(0).x(), x);
2869         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2870     }
2871     for (int i = 1; i <  3; ++i) {
2872         sendPreeditText(preeditText, preeditText.length() - i);
2873         QCOMPARE(input->positionToRectangle(0).x(), x);
2874         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2875     }
2876
2877     // Test disabling auto scroll.
2878     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2879
2880     input->setAutoScroll(false);
2881     sendPreeditText(preeditText.mid(0, 3), 1);
2882     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2883     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2884 }
2885
2886 void tst_qquicktextinput::preeditCursorRectangle()
2887 {
2888     QString preeditText = "super";
2889
2890     QQuickView view(testFileUrl("inputMethodEvent.qml"));
2891     view.show();
2892     view.requestActivateWindow();
2893     QTest::qWaitForWindowShown(&view);
2894     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2895     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2896     QVERIFY(input);
2897
2898     QRect currentRect;
2899
2900     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2901     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2902     QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2903
2904     // Verify that the micro focus rect is positioned the same for position 0 as
2905     // it would be if there was no preedit text.
2906     sendPreeditText(preeditText, 0);
2907     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2908     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2909     QCOMPARE(currentRect, previousRect);
2910
2911     QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2912     QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2913
2914     // Verify that the micro focus rect moves to the left as the cursor position
2915     // is incremented.
2916     for (int i = 1; i <= 5; ++i) {
2917         sendPreeditText(preeditText, i);
2918         QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2919         currentRect = query.value(Qt::ImCursorRectangle).toRect();
2920         QVERIFY(previousRect.left() < currentRect.left());
2921         QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2922         QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2923         previousRect = currentRect;
2924     }
2925
2926     // Verify that if there is no preedit cursor then the micro focus rect is the
2927     // same as it would be if it were positioned at the end of the preedit text.
2928     sendPreeditText(preeditText, 0);
2929     QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2930     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2931     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2932     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2933     QCOMPARE(currentRect, previousRect);
2934     QVERIFY(inputSpy.count() > 0);
2935     QVERIFY(panelSpy.count() > 0);
2936 }
2937
2938 void tst_qquicktextinput::inputContextMouseHandler()
2939 {
2940     PlatformInputContext platformInputContext;
2941     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2942     inputPanelPrivate->testContext = &platformInputContext;
2943
2944     QString text = "supercalifragisiticexpialidocious!";
2945     QQuickView view(testFileUrl("inputContext.qml"));
2946     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2947     QVERIFY(input);
2948
2949     input->setFocus(true);
2950     input->setText("");
2951
2952     view.show();
2953     view.requestActivateWindow();
2954     QTest::qWaitForWindowShown(&view);
2955     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2956
2957     QTextLayout layout(text);
2958     layout.setFont(input->font());
2959     if (!qmlDisableDistanceField()) {
2960         QTextOption option;
2961         option.setUseDesignMetrics(true);
2962         layout.setTextOption(option);
2963     }
2964     layout.beginLayout();
2965     QTextLine line = layout.createLine();
2966     layout.endLayout();
2967
2968     const qreal x = line.cursorToX(2, QTextLine::Leading);
2969     const qreal y = line.height() / 2;
2970     QPoint position = QPointF(x, y).toPoint();
2971
2972     QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2973     QGuiApplication::sendEvent(input, &inputEvent);
2974
2975     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2976     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2977     QGuiApplication::processEvents();
2978
2979     QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2980     QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2981     QCOMPARE(platformInputContext.m_cursorPosition, 2);
2982 }
2983
2984 void tst_qquicktextinput::inputMethodComposing()
2985 {
2986     QString text = "supercalifragisiticexpialidocious!";
2987
2988     QQuickView view(testFileUrl("inputContext.qml"));
2989     view.show();
2990     view.requestActivateWindow();
2991     QTest::qWaitForWindowShown(&view);
2992     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2993     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2994     QVERIFY(input);
2995     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2996
2997     QCOMPARE(input->isInputMethodComposing(), false);
2998     {
2999         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3000         QGuiApplication::sendEvent(input, &event);
3001     }
3002     QCOMPARE(input->isInputMethodComposing(), true);
3003     QCOMPARE(spy.count(), 1);
3004
3005     {
3006         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3007         QGuiApplication::sendEvent(input, &event);
3008     }
3009     QCOMPARE(spy.count(), 1);
3010
3011     {
3012         QInputMethodEvent event;
3013         QGuiApplication::sendEvent(input, &event);
3014     }
3015     QCOMPARE(input->isInputMethodComposing(), false);
3016     QCOMPARE(spy.count(), 2);
3017 }
3018
3019 void tst_qquicktextinput::inputPanelUpdate()
3020 {
3021     PlatformInputContext platformInputContext;
3022     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
3023     inputPanelPrivate->testContext = &platformInputContext;
3024
3025     QQuickView view(testFileUrl("inputContext.qml"));
3026     view.show();
3027     view.requestActivateWindow();
3028     QTest::qWaitForWindowShown(&view);
3029     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3030     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3031     QVERIFY(input);
3032
3033     // text change even without cursor position change needs to trigger update
3034     input->setText("test");
3035     platformInputContext.clear();
3036     input->setText("xxxx");
3037     QVERIFY(platformInputContext.m_updateCallCount > 0);
3038
3039     // input method event replacing text
3040     platformInputContext.clear();
3041     {
3042         QInputMethodEvent inputMethodEvent;
3043         inputMethodEvent.setCommitString("y", -1, 1);
3044         QGuiApplication::sendEvent(input, &inputMethodEvent);
3045     }
3046     QVERIFY(platformInputContext.m_updateCallCount > 0);
3047
3048     // input method changing selection
3049     platformInputContext.clear();
3050     {
3051         QList<QInputMethodEvent::Attribute> attributes;
3052         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3053         QInputMethodEvent inputMethodEvent("", attributes);
3054         QGuiApplication::sendEvent(input, &inputMethodEvent);
3055     }
3056     QVERIFY(input->selectionStart() != input->selectionEnd());
3057     QVERIFY(platformInputContext.m_updateCallCount > 0);
3058
3059     // programmatical selections trigger update
3060     platformInputContext.clear();
3061     input->selectAll();
3062     QVERIFY(platformInputContext.m_updateCallCount > 0);
3063
3064     // font changes
3065     platformInputContext.clear();
3066     QFont font = input->font();
3067     font.setBold(!font.bold());
3068     input->setFont(font);
3069     QVERIFY(platformInputContext.m_updateCallCount > 0);
3070
3071     // normal input
3072     platformInputContext.clear();
3073     {
3074         QInputMethodEvent inputMethodEvent;
3075         inputMethodEvent.setCommitString("y");
3076         QGuiApplication::sendEvent(input, &inputMethodEvent);
3077     }
3078     QVERIFY(platformInputContext.m_updateCallCount > 0);
3079
3080     // changing cursor position
3081     platformInputContext.clear();
3082     input->setCursorPosition(0);
3083     QVERIFY(platformInputContext.m_updateCallCount > 0);
3084 }
3085
3086 void tst_qquicktextinput::cursorRectangleSize()
3087 {
3088     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3089     QVERIFY(canvas->rootObject() != 0);
3090     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3091
3092     // make sure cursor rectangle is not at (0,0)
3093     textInput->setX(10);
3094     textInput->setY(10);
3095     textInput->setCursorPosition(3);
3096     QVERIFY(textInput != 0);
3097     textInput->setFocus(true);
3098     canvas->show();
3099     canvas->requestActivateWindow();
3100     QTest::qWaitForWindowShown(canvas);
3101
3102     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3103     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
3104     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3105
3106     QRect cursorRectFromItem = textInput->cursorRectangle();
3107     QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3108
3109     // item and input query cursor rectangles match
3110     QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
3111
3112     // item cursor rectangle and positionToRectangle calculations match
3113     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
3114
3115     // item-canvas transform and input item transform match
3116     QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
3117
3118     // input panel cursorRectangle property and tranformed item cursor rectangle match
3119     QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3120     QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
3121
3122     delete canvas;
3123 }
3124
3125 void tst_qquicktextinput::tripleClickSelectsAll()
3126 {
3127     QString qmlfile = testFile("positionAt.qml");
3128     QQuickView view(QUrl::fromLocalFile(qmlfile));
3129     view.show();
3130     view.requestActivateWindow();
3131     QTest::qWaitForWindowShown(&view);
3132
3133     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3134
3135     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3136     QVERIFY(input);
3137
3138     QLatin1String hello("Hello world!");
3139     input->setSelectByMouse(true);
3140     input->setText(hello);
3141
3142     // Clicking on the same point inside TextInput three times in a row
3143     // should trigger a triple click, thus selecting all the text.
3144     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3145     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3146     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3147     QGuiApplication::processEvents();
3148     QCOMPARE(input->selectedText(), hello);
3149
3150     // Now it simulates user moving the mouse between the second and the third click.
3151     // In this situation, we don't expect a triple click.
3152     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3153     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3154     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3155     QGuiApplication::processEvents();
3156     QVERIFY(input->selectedText().isEmpty());
3157
3158     // And now we press the third click too late, so no triple click event is triggered.
3159     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3160     QGuiApplication::processEvents();
3161     QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3162     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3163     QGuiApplication::processEvents();
3164     QVERIFY(input->selectedText().isEmpty());
3165 }
3166
3167 void tst_qquicktextinput::QTBUG_19956_data()
3168 {
3169     QTest::addColumn<QString>("url");
3170     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3171     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3172 }
3173
3174
3175 void tst_qquicktextinput::getText_data()
3176 {
3177     QTest::addColumn<QString>("text");
3178     QTest::addColumn<QString>("inputMask");
3179     QTest::addColumn<int>("start");
3180     QTest::addColumn<int>("end");
3181     QTest::addColumn<QString>("expectedText");
3182
3183     QTest::newRow("all plain text")
3184             << standard.at(0)
3185             << QString()
3186             << 0 << standard.at(0).length()
3187             << standard.at(0);
3188
3189     QTest::newRow("plain text sub string")
3190             << standard.at(0)
3191             << QString()
3192             << 0 << 12
3193             << standard.at(0).mid(0, 12);
3194
3195     QTest::newRow("plain text sub string reversed")
3196             << standard.at(0)
3197             << QString()
3198             << 12 << 0
3199             << standard.at(0).mid(0, 12);
3200
3201     QTest::newRow("plain text cropped beginning")
3202             << standard.at(0)
3203             << QString()
3204             << -3 << 4
3205             << standard.at(0).mid(0, 4);
3206
3207     QTest::newRow("plain text cropped end")
3208             << standard.at(0)
3209             << QString()
3210             << 23 << standard.at(0).length() + 8
3211             << standard.at(0).mid(23);
3212
3213     QTest::newRow("plain text cropped beginning and end")
3214             << standard.at(0)
3215             << QString()
3216             << -9 << standard.at(0).length() + 4
3217             << standard.at(0);
3218 }
3219
3220 void tst_qquicktextinput::getText()
3221 {
3222     QFETCH(QString, text);
3223     QFETCH(QString, inputMask);
3224     QFETCH(int, start);
3225     QFETCH(int, end);
3226     QFETCH(QString, expectedText);
3227
3228     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3229     QDeclarativeComponent textInputComponent(&engine);
3230     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3231     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3232     QVERIFY(textInput != 0);
3233
3234     QCOMPARE(textInput->getText(start, end), expectedText);
3235 }
3236
3237 void tst_qquicktextinput::insert_data()
3238 {
3239     QTest::addColumn<QString>("text");
3240     QTest::addColumn<QString>("inputMask");
3241     QTest::addColumn<int>("selectionStart");
3242     QTest::addColumn<int>("selectionEnd");
3243     QTest::addColumn<int>("insertPosition");
3244     QTest::addColumn<QString>("insertText");
3245     QTest::addColumn<QString>("expectedText");
3246     QTest::addColumn<int>("expectedSelectionStart");
3247     QTest::addColumn<int>("expectedSelectionEnd");
3248     QTest::addColumn<int>("expectedCursorPosition");
3249     QTest::addColumn<bool>("selectionChanged");
3250     QTest::addColumn<bool>("cursorPositionChanged");
3251
3252     QTest::newRow("at cursor position (beginning)")
3253             << standard.at(0)
3254             << QString()
3255             << 0 << 0 << 0
3256             << QString("Hello")
3257             << QString("Hello") + standard.at(0)
3258             << 5 << 5 << 5
3259             << false << true;
3260
3261     QTest::newRow("at cursor position (end)")
3262             << standard.at(0)
3263             << QString()
3264             << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3265             << QString("Hello")
3266             << standard.at(0) + QString("Hello")
3267             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3268             << false << true;
3269
3270     QTest::newRow("at cursor position (middle)")
3271             << standard.at(0)
3272             << QString()
3273             << 18 << 18 << 18
3274             << QString("Hello")
3275             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3276             << 23 << 23 << 23
3277             << false << true;
3278
3279     QTest::newRow("after cursor position (beginning)")
3280             << standard.at(0)
3281             << QString()
3282             << 0 << 0 << 18
3283             << QString("Hello")
3284             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3285             << 0 << 0 << 0
3286             << false << false;
3287
3288     QTest::newRow("before cursor position (end)")
3289             << standard.at(0)
3290             << QString()
3291             << standard.at(0).length() << standard.at(0).length() << 18
3292             << QString("Hello")
3293             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3294             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3295             << false << true;
3296
3297     QTest::newRow("before cursor position (middle)")
3298             << standard.at(0)
3299             << QString()
3300             << 18 << 18 << 0
3301             << QString("Hello")
3302             << QString("Hello") + standard.at(0)
3303             << 23 << 23 << 23
3304             << false << true;
3305
3306     QTest::newRow("after cursor position (middle)")
3307             << standard.at(0)
3308             << QString()
3309             << 18 << 18 << standard.at(0).length()
3310             << QString("Hello")
3311             << standard.at(0) + QString("Hello")
3312             << 18 << 18 << 18
3313             << false << false;
3314
3315     QTest::newRow("before selection")
3316             << standard.at(0)
3317             << QString()
3318             << 14 << 19 << 0
3319             << QString("Hello")
3320             << QString("Hello") + standard.at(0)
3321             << 19 << 24 << 24
3322             << false << true;
3323
3324     QTest::newRow("before reversed selection")
3325             << standard.at(0)
3326             << QString()
3327             << 19 << 14 << 0
3328             << QString("Hello")
3329             << QString("Hello") + standard.at(0)
3330             << 19 << 24 << 19
3331             << false << true;
3332
3333     QTest::newRow("after selection")
3334             << standard.at(0)
3335             << QString()
3336             << 14 << 19 << standard.at(0).length()
3337             << QString("Hello")
3338             << standard.at(0) + QString("Hello")
3339             << 14 << 19 << 19
3340             << false << false;
3341
3342     QTest::newRow("after reversed selection")
3343             << standard.at(0)
3344             << QString()
3345             << 19 << 14 << standard.at(0).length()
3346             << QString("Hello")
3347             << standard.at(0) + QString("Hello")
3348             << 14 << 19 << 14
3349             << false << false;
3350
3351     QTest::newRow("into selection")
3352             << standard.at(0)
3353             << QString()
3354             << 14 << 19 << 18
3355             << QString("Hello")
3356             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3357             << 14 << 24 << 24
3358             << true << true;
3359
3360     QTest::newRow("into reversed selection")
3361             << standard.at(0)
3362             << QString()
3363             << 19 << 14 << 18
3364             << QString("Hello")
3365             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3366             << 14 << 24 << 14
3367             << true << false;
3368
3369     QTest::newRow("rich text into plain text")
3370             << standard.at(0)
3371             << QString()
3372             << 0 << 0 << 0
3373             << QString("<b>Hello</b>")
3374             << QString("<b>Hello</b>") + standard.at(0)
3375             << 12 << 12 << 12
3376             << false << true;
3377
3378     QTest::newRow("before start")
3379             << standard.at(0)
3380             << QString()
3381             << 0 << 0 << -3
3382             << QString("Hello")
3383             << standard.at(0)
3384             << 0 << 0 << 0
3385             << false << false;
3386
3387     QTest::newRow("past end")
3388             << standard.at(0)
3389             << QString()
3390             << 0 << 0 << standard.at(0).length() + 3
3391             << QString("Hello")
3392             << standard.at(0)
3393             << 0 << 0 << 0
3394             << false << false;
3395
3396     const QString inputMask = "009.009.009.009";
3397     const QString ip = "192.168.2.14";
3398
3399     QTest::newRow("mask: at cursor position (beginning)")
3400             << ip
3401             << inputMask
3402             << 0 << 0 << 0
3403             << QString("125")
3404             << QString("125.168.2.14")
3405             << 0 << 0 << 0
3406             << false << false;
3407
3408     QTest::newRow("mask: at cursor position (end)")
3409             << ip
3410             << inputMask
3411             << inputMask.length() << inputMask.length() << inputMask.length()
3412             << QString("8")
3413             << ip
3414             << inputMask.length() << inputMask.length() << inputMask.length()
3415             << false << false;
3416
3417     QTest::newRow("mask: at cursor position (middle)")
3418             << ip
3419             << inputMask
3420             << 6 << 6 << 6
3421             << QString("75.2")
3422             << QString("192.167.5.24")
3423             << 6 << 6 << 6
3424             << false << false;
3425
3426     QTest::newRow("mask: after cursor position (beginning)")
3427             << ip
3428             << inputMask
3429             << 0 << 0 << 6
3430             << QString("75.2")
3431             << QString("192.167.5.24")
3432             << 0 << 0 << 0
3433             << false << false;
3434
3435     QTest::newRow("mask: before cursor position (end)")
3436             << ip
3437             << inputMask
3438             << inputMask.length() << inputMask.length() << 6
3439             << QString("75.2")
3440             << QString("192.167.5.24")
3441             << inputMask.length() << inputMask.length() << inputMask.length()
3442             << false << false;
3443
3444     QTest::newRow("mask: before cursor position (middle)")
3445             << ip
3446             << inputMask
3447             << 6 << 6 << 0
3448             << QString("125")
3449             << QString("125.168.2.14")
3450             << 6 << 6 << 6
3451             << false << false;
3452
3453     QTest::newRow("mask: after cursor position (middle)")
3454             << ip
3455             << inputMask
3456             << 6 << 6 << 13
3457             << QString("8")
3458             << "192.168.2.18"
3459             << 6 << 6 << 6
3460             << false << false;
3461
3462     QTest::newRow("mask: before selection")
3463             << ip
3464             << inputMask
3465             << 6 << 8 << 0
3466             << QString("125")
3467             << QString("125.168.2.14")
3468             << 6 << 8 << 8
3469             << false << false;
3470
3471     QTest::newRow("mask: before reversed selection")
3472             << ip
3473             << inputMask
3474             << 8 << 6 << 0
3475             << QString("125")
3476             << QString("125.168.2.14")
3477             << 6 << 8 << 6
3478             << false << false;
3479
3480     QTest::newRow("mask: after selection")
3481             << ip
3482             << inputMask
3483             << 6 << 8 << 13
3484             << QString("8")
3485             << "192.168.2.18"
3486             << 6 << 8 << 8
3487             << false << false;
3488
3489     QTest::newRow("mask: after reversed selection")
3490             << ip
3491             << inputMask
3492             << 8 << 6 << 13
3493             << QString("8")
3494             << "192.168.2.18"
3495             << 6 << 8 << 6
3496             << false << false;
3497
3498     QTest::newRow("mask: into selection")
3499             << ip
3500             << inputMask
3501             << 5 << 8 << 6
3502             << QString("75.2")
3503             << QString("192.167.5.24")
3504             << 5 << 8 << 8
3505             << true << false;
3506
3507     QTest::newRow("mask: into reversed selection")
3508             << ip
3509             << inputMask
3510             << 8 << 5 << 6
3511             << QString("75.2")
3512             << QString("192.167.5.24")
3513             << 5 << 8 << 5
3514             << true << false;
3515
3516     QTest::newRow("mask: before start")
3517             << ip
3518             << inputMask
3519             << 0 << 0 << -3
3520             << QString("4")
3521             << ip
3522             << 0 << 0 << 0
3523             << false << false;
3524
3525     QTest::newRow("mask: past end")
3526             << ip
3527             << inputMask
3528             << 0 << 0 << ip.length() + 3
3529             << QString("4")
3530             << ip
3531             << 0 << 0 << 0
3532             << false << false;
3533
3534     QTest::newRow("mask: invalid characters")
3535             << ip
3536             << inputMask
3537             << 0 << 0 << 0
3538             << QString("abc")
3539             << QString("192.168.2.14")
3540             << 0 << 0 << 0
3541             << false << false;
3542
3543     QTest::newRow("mask: mixed validity")
3544             << ip
3545             << inputMask
3546             << 0 << 0 << 0
3547             << QString("a1b2c5")
3548             << QString("125.168.2.14")
3549             << 0 << 0 << 0
3550             << false << false;
3551 }
3552
3553 void tst_qquicktextinput::insert()
3554 {
3555     QFETCH(QString, text);
3556     QFETCH(QString, inputMask);
3557     QFETCH(int, selectionStart);
3558     QFETCH(int, selectionEnd);
3559     QFETCH(int, insertPosition);
3560     QFETCH(QString, insertText);
3561     QFETCH(QString, expectedText);
3562     QFETCH(int, expectedSelectionStart);
3563     QFETCH(int, expectedSelectionEnd);
3564     QFETCH(int, expectedCursorPosition);
3565     QFETCH(bool, selectionChanged);
3566     QFETCH(bool, cursorPositionChanged);
3567
3568     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3569     QDeclarativeComponent textInputComponent(&engine);
3570     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3571     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3572     QVERIFY(textInput != 0);
3573
3574     textInput->select(selectionStart, selectionEnd);
3575
3576     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3577     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3578     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3579     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3580     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3581
3582     textInput->insert(insertPosition, insertText);
3583
3584     QCOMPARE(textInput->text(), expectedText);
3585     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3586
3587     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3588     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3589     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3590
3591     if (selectionStart > selectionEnd)
3592         qSwap(selectionStart, selectionEnd);
3593
3594     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3595     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3596     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3597     QCOMPARE(textSpy.count() > 0, text != expectedText);
3598     QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3599 }
3600
3601 void tst_qquicktextinput::remove_data()
3602 {
3603     QTest::addColumn<QString>("text");
3604     QTest::addColumn<QString>("inputMask");
3605     QTest::addColumn<int>("selectionStart");
3606     QTest::addColumn<int>("selectionEnd");
3607     QTest::addColumn<int>("removeStart");
3608     QTest::addColumn<int>("removeEnd");
3609     QTest::addColumn<QString>("expectedText");
3610     QTest::addColumn<int>("expectedSelectionStart");
3611     QTest::addColumn<int>("expectedSelectionEnd");
3612     QTest::addColumn<int>("expectedCursorPosition");
3613     QTest::addColumn<bool>("selectionChanged");
3614     QTest::addColumn<bool>("cursorPositionChanged");
3615
3616     QTest::newRow("from cursor position (beginning)")
3617             << standard.at(0)
3618             << QString()
3619             << 0 << 0
3620             << 0 << 5
3621             << standard.at(0).mid(5)
3622             << 0 << 0 << 0
3623             << false << false;
3624
3625     QTest::newRow("to cursor position (beginning)")
3626             << standard.at(0)
3627             << QString()
3628             << 0 << 0
3629             << 5 << 0
3630             << standard.at(0).mid(5)
3631             << 0 << 0 << 0
3632             << false << false;
3633
3634     QTest::newRow("to cursor position (end)")
3635             << standard.at(0)
3636             << QString()
3637             << standard.at(0).length() << standard.at(0).length()
3638             << standard.at(0).length() << standard.at(0).length() - 5
3639             << standard.at(0).mid(0, standard.at(0).length() - 5)
3640             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3641             << false << true;
3642
3643     QTest::newRow("to cursor position (end)")
3644             << standard.at(0)
3645             << QString()
3646             << standard.at(0).length() << standard.at(0).length()
3647             << standard.at(0).length() - 5 << standard.at(0).length()
3648             << standard.at(0).mid(0, standard.at(0).length() - 5)
3649             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3650             << false << true;
3651
3652     QTest::newRow("from cursor position (middle)")
3653             << standard.at(0)
3654             << QString()
3655             << 18 << 18
3656             << 18 << 23
3657             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3658             << 18 << 18 << 18
3659             << false << false;
3660
3661     QTest::newRow("to cursor position (middle)")
3662             << standard.at(0)
3663             << QString()
3664             << 23 << 23
3665             << 18 << 23
3666             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3667             << 18 << 18 << 18
3668             << false << true;
3669
3670     QTest::newRow("after cursor position (beginning)")
3671             << standard.at(0)
3672             << QString()
3673             << 0 << 0
3674             << 18 << 23
3675             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3676             << 0 << 0 << 0
3677             << false << false;
3678
3679     QTest::newRow("before cursor position (end)")
3680             << standard.at(0)
3681             << QString()
3682             << standard.at(0).length() << standard.at(0).length()
3683             << 18 << 23
3684             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3685             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3686             << false << true;
3687
3688     QTest::newRow("before cursor position (middle)")
3689             << standard.at(0)
3690             << QString()
3691             << 23 << 23
3692             << 0 << 5
3693             << standard.at(0).mid(5)
3694             << 18 << 18 << 18
3695             << false << true;
3696
3697     QTest::newRow("after cursor position (middle)")
3698             << standard.at(0)
3699             << QString()
3700             << 18 << 18
3701             << 18 << 23
3702             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3703             << 18 << 18 << 18
3704             << false << false;
3705
3706     QTest::newRow("before selection")
3707             << standard.at(0)
3708             << QString()
3709             << 14 << 19
3710             << 0 << 5
3711             << standard.at(0).mid(5)
3712             << 9 << 14 << 14
3713             << false << true;
3714
3715     QTest::newRow("before reversed selection")
3716             << standard.at(0)
3717             << QString()
3718             << 19 << 14
3719             << 0 << 5
3720             << standard.at(0).mid(5)
3721             << 9 << 14 << 9
3722             << false << true;
3723
3724     QTest::newRow("after selection")
3725             << standard.at(0)
3726             << QString()
3727             << 14 << 19
3728             << standard.at(0).length() - 5 << standard.at(0).length()
3729             << standard.at(0).mid(0, standard.at(0).length() - 5)
3730             << 14 << 19 << 19
3731             << false << false;
3732
3733     QTest::newRow("after reversed selection")
3734             << standard.at(0)
3735             << QString()
3736             << 19 << 14
3737             << standard.at(0).length() - 5 << standard.at(0).length()
3738             << standard.at(0).mid(0, standard.at(0).length() - 5)
3739             << 14 << 19 << 14
3740             << false << false;
3741
3742     QTest::newRow("from selection")
3743             << standard.at(0)
3744             << QString()
3745             << 14 << 24
3746             << 18 << 23
3747             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3748             << 14 << 19 << 19
3749             << true << true;
3750
3751     QTest::newRow("from reversed selection")
3752             << standard.at(0)
3753             << QString()
3754             << 24 << 14
3755             << 18 << 23
3756             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3757             << 14 << 19 << 14
3758             << true << false;
3759
3760     QTest::newRow("cropped beginning")
3761             << standard.at(0)
3762             << QString()
3763             << 0 << 0
3764             << -3 << 4
3765             << standard.at(0).mid(4)
3766             << 0 << 0 << 0
3767             << false << false;
3768
3769     QTest::newRow("cropped end")
3770             << standard.at(0)
3771             << QString()
3772             << 0 << 0
3773             << 23 << standard.at(0).length() + 8
3774             << standard.at(0).mid(0, 23)
3775             << 0 << 0 << 0
3776             << false << false;
3777
3778     QTest::newRow("cropped beginning and end")
3779             << standard.at(0)
3780             << QString()
3781             << 0 << 0
3782             << -9 << standard.at(0).length() + 4
3783             << QString()
3784             << 0 << 0 << 0
3785             << false << false;
3786
3787     const QString inputMask = "009.009.009.009";
3788     const QString ip = "192.168.2.14";
3789
3790     QTest::newRow("mask: from cursor position")
3791             << ip
3792             << inputMask
3793             << 6 << 6
3794             << 6 << 9
3795             << QString("192.16..14")
3796             << 6 << 6 << 6
3797             << false << false;
3798
3799     QTest::newRow("mask: to cursor position")
3800             << ip
3801             << inputMask
3802             << 6 << 6
3803             << 2 << 6
3804             << QString("19.8.2.14")
3805             << 6 << 6 << 6
3806             << false << false;
3807
3808     QTest::newRow("mask: before cursor position")
3809             << ip
3810             << inputMask
3811             << 6 << 6
3812             << 0 << 2
3813             << QString("2.168.2.14")
3814             << 6 << 6 << 6
3815             << false << false;
3816
3817     QTest::newRow("mask: after cursor position")
3818             << ip
3819             << inputMask
3820             << 6 << 6
3821             << 12 << 16
3822             << QString("192.168.2.")
3823             << 6 << 6 << 6
3824             << false << false;
3825
3826     QTest::newRow("mask: before selection")
3827             << ip
3828             << inputMask
3829             << 6 << 8
3830             << 0 << 2
3831             << QString("2.168.2.14")
3832             << 6 << 8 << 8
3833             << false << false;
3834
3835     QTest::newRow("mask: before reversed selection")
3836             << ip
3837             << inputMask
3838             << 8 << 6
3839             << 0 << 2
3840             << QString("2.168.2.14")
3841             << 6 << 8 << 6
3842             << false << false;
3843
3844     QTest::newRow("mask: after selection")
3845             << ip
3846             << inputMask
3847             << 6 << 8
3848             << 12 << 16
3849             << QString("192.168.2.")
3850             << 6 << 8 << 8
3851             << false << false;
3852
3853     QTest::newRow("mask: after reversed selection")
3854             << ip
3855             << inputMask
3856             << 8 << 6
3857             << 12 << 16
3858             << QString("192.168.2.")
3859             << 6 << 8 << 6
3860             << false << false;
3861
3862     QTest::newRow("mask: from selection")
3863             << ip
3864             << inputMask
3865             << 6 << 13
3866             << 8 << 10
3867             << QString("192.168..14")
3868             << 6 << 13 << 13
3869             << true << false;
3870
3871     QTest::newRow("mask: from reversed selection")
3872             << ip
3873             << inputMask
3874             << 13 << 6
3875             << 8 << 10
3876             << QString("192.168..14")
3877             << 6 << 13 << 6
3878             << true << false;
3879
3880     QTest::newRow("mask: cropped beginning")
3881             << ip
3882             << inputMask
3883             << 0 << 0
3884             << -3 << 4
3885             << QString(".168.2.14")
3886             << 0 << 0 << 0
3887             << false << false;
3888
3889     QTest::newRow("mask: cropped end")
3890             << ip
3891             << inputMask
3892             << 0 << 0
3893             << 13 << 28
3894             << QString("192.168.2.1")
3895             << 0 << 0 << 0
3896             << false << false;
3897
3898     QTest::newRow("mask: cropped beginning and end")
3899             << ip
3900             << inputMask
3901             << 0 << 0
3902             << -9 << 28
3903             << QString("...")
3904             << 0 << 0 << 0
3905             << false << false;
3906 }
3907
3908 void tst_qquicktextinput::remove()
3909 {
3910     QFETCH(QString, text);
3911     QFETCH(QString, inputMask);
3912     QFETCH(int, selectionStart);
3913     QFETCH(int, selectionEnd);
3914     QFETCH(int, removeStart);
3915     QFETCH(int, removeEnd);
3916     QFETCH(QString, expectedText);
3917     QFETCH(int, expectedSelectionStart);
3918     QFETCH(int, expectedSelectionEnd);
3919     QFETCH(int, expectedCursorPosition);
3920     QFETCH(bool, selectionChanged);
3921     QFETCH(bool, cursorPositionChanged);
3922
3923     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3924     QDeclarativeComponent textInputComponent(&engine);
3925     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3926     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3927     QVERIFY(textInput != 0);
3928
3929     textInput->select(selectionStart, selectionEnd);
3930
3931     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3932     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3933     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3934     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3935     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3936
3937     textInput->remove(removeStart, removeEnd);
3938
3939     QCOMPARE(textInput->text(), expectedText);
3940     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3941
3942     if (selectionStart > selectionEnd)  //
3943         qSwap(selectionStart, selectionEnd);
3944
3945     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3946     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3947     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3948
3949     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3950     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3951     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3952     QCOMPARE(textSpy.count() > 0, text != expectedText);
3953
3954     if (cursorPositionChanged)  //
3955         QVERIFY(cursorPositionSpy.count() > 0);
3956 }
3957
3958 void tst_qquicktextinput::keySequence_data()
3959 {
3960     QTest::addColumn<QString>("text");
3961     QTest::addColumn<QKeySequence>("sequence");
3962     QTest::addColumn<int>("selectionStart");
3963     QTest::addColumn<int>("selectionEnd");
3964     QTest::addColumn<int>("cursorPosition");
3965     QTest::addColumn<QString>("expectedText");
3966     QTest::addColumn<QString>("selectedText");
3967
3968     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3969
3970     QTest::newRow("select all")
3971             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3972             << 44 << standard.at(0) << standard.at(0);
3973     QTest::newRow("select end of line")
3974             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3975             << 44 << standard.at(0) << standard.at(0).mid(5);
3976     QTest::newRow("select end of document") // ### Not handled.
3977             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3978             << 3 << standard.at(0) << QString();
3979     QTest::newRow("select end of block")
3980             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3981             << 44 << standard.at(0) << standard.at(0).mid(18);
3982     QTest::newRow("delete end of line")
3983             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3984             << 24 << standard.at(0).mid(0, 24) << QString();
3985     QTest::newRow("move to start of line")
3986             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3987             << 0 << standard.at(0) << QString();
3988     QTest::newRow("move to start of block")
3989             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3990             << 0 << standard.at(0) << QString();
3991     QTest::newRow("move to next char")
3992             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3993             << 13 << standard.at(0) << QString();
3994     QTest::newRow("move to previous char")
3995             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3996             << 2 << standard.at(0) << QString();
3997     QTest::newRow("select next char")
3998             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
3999             << 24 << standard.at(0) << standard.at(0).mid(23, 1);
4000     QTest::newRow("select previous char")
4001             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4002             << 18 << standard.at(0) << standard.at(0).mid(18, 1);
4003     QTest::newRow("move to next word")
4004             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4005             << 10 << standard.at(0) << QString();
4006     QTest::newRow("move to previous word")
4007             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4008             << 4 << standard.at(0) << QString();
4009     QTest::newRow("select previous word")
4010             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4011             << 10 << standard.at(0) << standard.at(0).mid(10, 1);
4012     QTest::newRow("delete (selection)")
4013             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4014             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
4015     QTest::newRow("delete (no selection)")
4016             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4017             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
4018     QTest::newRow("delete end of word")
4019             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4020             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
4021     QTest::newRow("delete start of word")
4022             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4023             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
4024 }
4025
4026 void tst_qquicktextinput::keySequence()
4027 {
4028     QFETCH(QString, text);
4029     QFETCH(QKeySequence, sequence);
4030     QFETCH(int, selectionStart);
4031     QFETCH(int, selectionEnd);
4032     QFETCH(int, cursorPosition);
4033     QFETCH(QString, expectedText);
4034     QFETCH(QString, selectedText);
4035
4036     if (sequence.isEmpty()) {
4037         QSKIP("Key sequence is undefined");
4038     }
4039
4040     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4041     QDeclarativeComponent textInputComponent(&engine);
4042     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4043     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4044     QVERIFY(textInput != 0);
4045
4046     QQuickCanvas canvas;
4047     textInput->setParentItem(canvas.rootItem());
4048     canvas.show();
4049     canvas.requestActivateWindow();
4050     QTest::qWaitForWindowShown(&canvas);
4051     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4052
4053     textInput->select(selectionStart, selectionEnd);
4054
4055     simulateKeys(&canvas, sequence);
4056
4057     QCOMPARE(textInput->cursorPosition(), cursorPosition);
4058     QCOMPARE(textInput->text(), expectedText);
4059     QCOMPARE(textInput->selectedText(), selectedText);
4060 }
4061
4062 #define NORMAL 0
4063 #define REPLACE_UNTIL_END 1
4064
4065 void tst_qquicktextinput::undo_data()
4066 {
4067     QTest::addColumn<QStringList>("insertString");
4068     QTest::addColumn<IntList>("insertIndex");
4069     QTest::addColumn<IntList>("insertMode");
4070     QTest::addColumn<QStringList>("expectedString");
4071     QTest::addColumn<bool>("use_keys");
4072
4073     for (int i=0; i<2; i++) {
4074         QString keys_str = "keyboard";
4075         bool use_keys = true;
4076         if (i==0) {
4077             keys_str = "insert";
4078             use_keys = false;
4079         }
4080
4081         {
4082             IntList insertIndex;
4083             IntList insertMode;
4084             QStringList insertString;
4085             QStringList expectedString;
4086
4087             insertIndex << -1;
4088             insertMode << NORMAL;
4089             insertString << "1";
4090
4091             insertIndex << -1;
4092             insertMode << NORMAL;
4093             insertString << "5";
4094
4095             insertIndex << 1;
4096             insertMode << NORMAL;
4097             insertString << "3";
4098
4099             insertIndex << 1;
4100             insertMode << NORMAL;
4101             insertString << "2";
4102
4103             insertIndex << 3;
4104             insertMode << NORMAL;
4105             insertString << "4";
4106
4107             expectedString << "12345";
4108             expectedString << "1235";
4109             expectedString << "135";
4110             expectedString << "15";
4111             expectedString << "";
4112
4113             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4114                 insertString <<
4115                 insertIndex <<
4116                 insertMode <<
4117                 expectedString <<
4118                 bool(use_keys);
4119         }
4120         {
4121             IntList insertIndex;
4122             IntList insertMode;
4123             QStringList insertString;
4124             QStringList expectedString;
4125
4126             insertIndex << -1;
4127             insertMode << NORMAL;
4128             insertString << "World"; // World
4129
4130             insertIndex << 0;
4131             insertMode << NORMAL;
4132             insertString << "Hello"; // HelloWorld
4133
4134             insertIndex << 0;
4135             insertMode << NORMAL;
4136             insertString << "Well"; // WellHelloWorld
4137
4138             insertIndex << 9;
4139             insertMode << NORMAL;
4140             insertString << "There"; // WellHelloThereWorld;
4141
4142             expectedString << "WellHelloThereWorld";
4143             expectedString << "WellHelloWorld";
4144             expectedString << "HelloWorld";
4145             expectedString << "World";
4146             expectedString << "";
4147
4148             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4149                 insertString <<
4150                 insertIndex <<
4151                 insertMode <<
4152                 expectedString <<
4153                 bool(use_keys);
4154         }
4155         {
4156             IntList insertIndex;
4157             IntList insertMode;
4158             QStringList insertString;
4159             QStringList expectedString;
4160
4161             insertIndex << -1;
4162             insertMode << NORMAL;
4163             insertString << "Ensuring";
4164
4165             insertIndex << -1;
4166             insertMode << NORMAL;
4167             insertString << " instan";
4168
4169             insertIndex << 9;
4170             insertMode << NORMAL;
4171             insertString << "an ";
4172
4173             insertIndex << 10;
4174             insertMode << REPLACE_UNTIL_END;
4175             insertString << " unique instance.";
4176
4177             expectedString << "Ensuring a unique instance.";
4178             expectedString << "Ensuring an instan";
4179             expectedString << "Ensuring instan";
4180             expectedString << "";
4181
4182             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4183                 insertString <<
4184                 insertIndex <<
4185                 insertMode <<
4186                 expectedString <<
4187                 bool(use_keys);
4188         }
4189     }
4190 }
4191
4192 void tst_qquicktextinput::undo()
4193 {
4194     QFETCH(QStringList, insertString);
4195     QFETCH(IntList, insertIndex);
4196     QFETCH(IntList, insertMode);
4197     QFETCH(QStringList, expectedString);
4198
4199     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4200     QDeclarativeComponent textInputComponent(&engine);
4201     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4202     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4203     QVERIFY(textInput != 0);
4204
4205     QQuickCanvas canvas;
4206     textInput->setParentItem(canvas.rootItem());
4207     canvas.show();
4208     canvas.requestActivateWindow();
4209     QTest::qWaitForWindowShown(&canvas);
4210     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4211
4212     QVERIFY(!textInput->canUndo());
4213
4214     QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4215
4216     int i;
4217
4218 // STEP 1: First build up an undo history by inserting or typing some strings...
4219     for (i = 0; i < insertString.size(); ++i) {
4220         if (insertIndex[i] > -1)
4221             textInput->setCursorPosition(insertIndex[i]);
4222
4223  // experimental stuff
4224         if (insertMode[i] == REPLACE_UNTIL_END) {
4225             textInput->select(insertIndex[i], insertIndex[i] + 8);
4226
4227             // This is what I actually want...
4228             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4229         }
4230
4231         for (int j = 0; j < insertString.at(i).length(); j++)
4232             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4233     }
4234
4235     QCOMPARE(spy.count(), 1);
4236
4237 // STEP 2: Next call undo several times and see if we can restore to the previous state
4238     for (i = 0; i < expectedString.size() - 1; ++i) {
4239         QCOMPARE(textInput->text(), expectedString[i]);
4240         QVERIFY(textInput->canUndo());
4241         textInput->undo();
4242     }
4243
4244 // STEP 3: Verify that we have undone everything
4245     QVERIFY(textInput->text().isEmpty());
4246     QVERIFY(!textInput->canUndo());
4247     QCOMPARE(spy.count(), 2);
4248 }
4249
4250 void tst_qquicktextinput::redo_data()
4251 {
4252     QTest::addColumn<QStringList>("insertString");
4253     QTest::addColumn<IntList>("insertIndex");
4254     QTest::addColumn<QStringList>("expectedString");
4255
4256     {
4257         IntList insertIndex;
4258         QStringList insertString;
4259         QStringList expectedString;
4260
4261         insertIndex << -1;
4262         insertString << "World"; // World
4263         insertIndex << 0;
4264         insertString << "Hello"; // HelloWorld
4265         insertIndex << 0;
4266         insertString << "Well"; // WellHelloWorld
4267         insertIndex << 9;
4268         insertString << "There"; // WellHelloThereWorld;
4269
4270         expectedString << "World";
4271         expectedString << "HelloWorld";
4272         expectedString << "WellHelloWorld";
4273         expectedString << "WellHelloThereWorld";
4274
4275         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4276     }
4277 }
4278
4279 void tst_qquicktextinput::redo()
4280 {
4281     QFETCH(QStringList, insertString);
4282     QFETCH(IntList, insertIndex);
4283     QFETCH(QStringList, expectedString);
4284
4285     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4286     QDeclarativeComponent textInputComponent(&engine);
4287     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4288     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4289     QVERIFY(textInput != 0);
4290
4291     QQuickCanvas canvas;
4292     textInput->setParentItem(canvas.rootItem());
4293     canvas.show();
4294     canvas.requestActivateWindow();
4295     QTest::qWaitForWindowShown(&canvas);
4296     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4297
4298     QVERIFY(!textInput->canUndo());
4299     QVERIFY(!textInput->canRedo());
4300
4301     QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
4302
4303     int i;
4304     // inserts the diff strings at diff positions
4305     for (i = 0; i < insertString.size(); ++i) {
4306         if (insertIndex[i] > -1)
4307             textInput->setCursorPosition(insertIndex[i]);
4308         for (int j = 0; j < insertString.at(i).length(); j++)
4309             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4310         QVERIFY(textInput->canUndo());
4311         QVERIFY(!textInput->canRedo());
4312     }
4313
4314     QCOMPARE(spy.count(), 0);
4315
4316     // undo everything
4317     while (!textInput->text().isEmpty()) {
4318         QVERIFY(textInput->canUndo());
4319         textInput->undo();
4320         QVERIFY(textInput->canRedo());
4321     }
4322
4323     QCOMPARE(spy.count(), 1);
4324
4325     for (i = 0; i < expectedString.size(); ++i) {
4326         QVERIFY(textInput->canRedo());
4327         textInput->redo();
4328         QCOMPARE(textInput->text() , expectedString[i]);
4329         QVERIFY(textInput->canUndo());
4330     }
4331     QVERIFY(!textInput->canRedo());
4332     QCOMPARE(spy.count(), 2);
4333 }
4334
4335 void tst_qquicktextinput::undo_keypressevents_data()
4336 {
4337     QTest::addColumn<KeyList>("keys");
4338     QTest::addColumn<QStringList>("expectedString");
4339
4340     {
4341         KeyList keys;
4342         QStringList expectedString;
4343
4344         keys << "AFRAID"
4345                 << QKeySequence::MoveToStartOfLine
4346                 << "VERY"
4347                 << Qt::Key_Left
4348                 << Qt::Key_Left
4349                 << Qt::Key_Left
4350                 << Qt::Key_Left
4351                 << "BE"
4352                 << QKeySequence::MoveToEndOfLine
4353                 << "!";
4354
4355         expectedString << "BEVERYAFRAID!";
4356         expectedString << "BEVERYAFRAID";
4357         expectedString << "VERYAFRAID";
4358         expectedString << "AFRAID";
4359
4360         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4361     } {
4362         KeyList keys;
4363         QStringList expectedString;
4364
4365         // inserting '1234'
4366         keys << "1234" << QKeySequence::MoveToStartOfLine
4367                 // skipping '12'
4368                 << Qt::Key_Right << Qt::Key_Right
4369                 // selecting '34'
4370                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4371                 // deleting '34'
4372                 << Qt::Key_Delete;
4373
4374         expectedString << "12";
4375         expectedString << "1234";
4376
4377         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4378     } {
4379         KeyList keys;
4380         QStringList expectedString;
4381
4382         // inserting 'AB12'
4383         keys << "AB12"
4384                 << QKeySequence::MoveToStartOfLine
4385                 // selecting 'AB'
4386                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4387                 << Qt::Key_Delete
4388                 << QKeySequence::Undo
4389                 << Qt::Key_Right
4390 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
4391                 << Qt::Key_Left
4392 #endif
4393                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4394                 << Qt::Key_Delete;
4395
4396         expectedString << "AB";
4397         expectedString << "AB12";
4398
4399         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4400     } {
4401         KeyList keys;
4402         QStringList expectedString;
4403
4404         // inserting 'ABCD'
4405         keys << "abcd"
4406                 //move left two
4407                 << Qt::Key_Left << Qt::Key_Left
4408                 // inserting '1234'
4409                 << "1234"
4410                 // selecting '1234'
4411                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4412                 // overwriting '1234' with '5'
4413                 << "5"
4414                 // undoing deletion of 'AB'
4415                 << QKeySequence::Undo
4416                 // overwriting '1234' with '6'
4417                 << "6";
4418
4419         expectedString << "ab6cd";
4420         // for versions previous to 3.2 we overwrite needed two undo operations
4421         expectedString << "ab1234cd";
4422         expectedString << "abcd";
4423
4424         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4425     } {
4426         KeyList keys;
4427         QStringList expectedString;
4428
4429         // inserting 'ABC'
4430         keys << "ABC"
4431                 // removes 'C'
4432                 << Qt::Key_Backspace;
4433
4434         expectedString << "AB";
4435         expectedString << "ABC";
4436
4437         QTest::newRow("Inserts,backspace") << keys << expectedString;
4438     } {
4439         KeyList keys;
4440         QStringList expectedString;
4441
4442         keys << "ABC"
4443                 // removes 'C'
4444                 << Qt::Key_Backspace
4445                 // inserting 'Z'
4446                 << "Z";
4447
4448         expectedString << "ABZ";
4449         expectedString << "AB";
4450         expectedString << "ABC";
4451
4452         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4453     } {
4454         KeyList keys;
4455         QStringList expectedString;
4456
4457         // inserting '123'
4458         keys << "123" << QKeySequence::MoveToStartOfLine
4459             // selecting '123'
4460              << QKeySequence::SelectEndOfLine
4461             // overwriting '123' with 'ABC'
4462              << "ABC";
4463
4464         expectedString << "ABC";
4465         // for versions previous to 3.2 we overwrite needed two undo operations
4466         expectedString << "123";
4467
4468         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4469     }
4470 }
4471
4472 void tst_qquicktextinput::undo_keypressevents()
4473 {
4474     QFETCH(KeyList, keys);
4475     QFETCH(QStringList, expectedString);
4476
4477     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4478     QDeclarativeComponent textInputComponent(&engine);
4479     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4480     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4481     QVERIFY(textInput != 0);
4482
4483     QQuickCanvas canvas;
4484     textInput->setParentItem(canvas.rootItem());
4485     canvas.show();
4486     canvas.requestActivateWindow();
4487     QTest::qWaitForWindowShown(&canvas);
4488     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4489
4490     simulateKeys(&canvas, keys);
4491
4492     for (int i = 0; i < expectedString.size(); ++i) {
4493         QCOMPARE(textInput->text() , expectedString[i]);
4494         textInput->undo();
4495     }
4496     QVERIFY(textInput->text().isEmpty());
4497 }
4498
4499 void tst_qquicktextinput::QTBUG_19956()
4500 {
4501     QFETCH(QString, url);
4502
4503     QQuickView canvas(testFileUrl(url));
4504     canvas.show();
4505     canvas.requestActivateWindow();
4506     QTest::qWaitForWindowShown(&canvas);
4507     QVERIFY(canvas.rootObject() != 0);
4508     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4509     QVERIFY(input);
4510     input->setFocus(true);
4511     QVERIFY(input->hasActiveFocus());
4512
4513     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
4514     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4515     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
4516     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4517
4518     canvas.rootObject()->setProperty("topvalue", 15);
4519     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
4520     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4521
4522     canvas.rootObject()->setProperty("topvalue", 25);
4523     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
4524     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4525
4526     canvas.rootObject()->setProperty("bottomvalue", 21);
4527     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
4528     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4529
4530     canvas.rootObject()->setProperty("bottomvalue", 10);
4531     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4532     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4533 }
4534
4535 void tst_qquicktextinput::QTBUG_19956_regexp()
4536 {
4537     QUrl url = testFileUrl("qtbug-19956regexp.qml");
4538
4539     QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
4540     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
4541
4542     QQuickView canvas(url);
4543     canvas.show();
4544     canvas.requestActivateWindow();
4545     QTest::qWaitForWindowShown(&canvas);
4546     QVERIFY(canvas.rootObject() != 0);
4547     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4548     QVERIFY(input);
4549     input->setFocus(true);
4550     QVERIFY(input->hasActiveFocus());
4551
4552     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4553     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4554     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
4555     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4556
4557     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
4558     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
4559     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4560
4561     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4562     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4563     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4564 }
4565
4566
4567 void tst_qquicktextinput::negativeDimensions()
4568 {
4569     // Verify this doesn't assert during initialization.
4570     QDeclarativeComponent component(&engine, testFileUrl("negativeDimensions.qml"));
4571     QScopedPointer<QObject> o(component.create());
4572     QVERIFY(o);
4573     QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
4574     QVERIFY(input);
4575     QCOMPARE(input->width(), qreal(-1));
4576     QCOMPARE(input->height(), qreal(-1));
4577 }
4578
4579 QTEST_MAIN(tst_qquicktextinput)
4580
4581 #include "tst_qquicktextinput.moc"