8fc768f2abadfdbfd5e8a21bd8d229ed0576cd89
[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 (int 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 (int 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     QInputMethodQueryEvent enabledQueryEvent(Qt::ImEnabled);
1956     QGuiApplication::sendEvent(input, &enabledQueryEvent);
1957     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), true);
1958
1959     input->setReadOnly(true);
1960     QGuiApplication::sendEvent(input, &enabledQueryEvent);
1961     QCOMPARE(enabledQueryEvent.value(Qt::ImEnabled).toBool(), false);
1962 }
1963
1964 /*
1965 TextInput element should only handle left/right keys until the cursor reaches
1966 the extent of the text, then they should ignore the keys.
1967
1968 */
1969 void tst_qquicktextinput::navigation()
1970 {
1971     QQuickView canvas(testFileUrl("navigation.qml"));
1972     canvas.show();
1973     canvas.requestActivateWindow();
1974
1975     QVERIFY(canvas.rootObject() != 0);
1976
1977     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1978
1979     QVERIFY(input != 0);
1980     input->setCursorPosition(0);
1981     QTRY_VERIFY(input->hasActiveFocus() == true);
1982     simulateKey(&canvas, Qt::Key_Left);
1983     QVERIFY(input->hasActiveFocus() == false);
1984     simulateKey(&canvas, Qt::Key_Right);
1985     QVERIFY(input->hasActiveFocus() == true);
1986     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1987     input->setCursorPosition(input->text().length());
1988     input->select(0,input->text().length());
1989     QVERIFY(input->selectionStart() != input->selectionEnd());
1990     simulateKey(&canvas, Qt::Key_Right);
1991     QVERIFY(input->selectionStart() == input->selectionEnd());
1992     QVERIFY(input->selectionStart() == input->text().length());
1993     QVERIFY(input->hasActiveFocus() == true);
1994     simulateKey(&canvas, Qt::Key_Right);
1995     QVERIFY(input->hasActiveFocus() == false);
1996     simulateKey(&canvas, Qt::Key_Left);
1997     QVERIFY(input->hasActiveFocus() == true);
1998
1999     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
2000     input->setCursorPosition(2);
2001     QCOMPARE(input->cursorPosition(),2);
2002     simulateKey(&canvas, Qt::Key_Up);
2003     QCOMPARE(input->cursorPosition(),2);
2004     simulateKey(&canvas, Qt::Key_Down);
2005     QCOMPARE(input->cursorPosition(),2);
2006 }
2007
2008 void tst_qquicktextinput::navigation_RTL()
2009 {
2010     QQuickView canvas(testFileUrl("navigation.qml"));
2011     canvas.show();
2012     canvas.requestActivateWindow();
2013
2014     QVERIFY(canvas.rootObject() != 0);
2015
2016     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2017
2018     QVERIFY(input != 0);
2019     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
2020     input->setText(QString::fromUtf16(arabic_str, 11));
2021
2022     input->setCursorPosition(0);
2023     QTRY_VERIFY(input->hasActiveFocus() == true);
2024
2025     // move off
2026     simulateKey(&canvas, Qt::Key_Right);
2027     QVERIFY(input->hasActiveFocus() == false);
2028
2029     // move back
2030     simulateKey(&canvas, Qt::Key_Left);
2031     QVERIFY(input->hasActiveFocus() == true);
2032
2033     input->setCursorPosition(input->text().length());
2034     QVERIFY(input->hasActiveFocus() == true);
2035
2036     // move off
2037     simulateKey(&canvas, Qt::Key_Left);
2038     QVERIFY(input->hasActiveFocus() == false);
2039
2040     // move back
2041     simulateKey(&canvas, Qt::Key_Right);
2042     QVERIFY(input->hasActiveFocus() == true);
2043 }
2044
2045 void tst_qquicktextinput::copyAndPaste() {
2046 #ifndef QT_NO_CLIPBOARD
2047
2048 #ifdef Q_OS_MAC
2049     {
2050         PasteboardRef pasteboard;
2051         OSStatus status = PasteboardCreate(0, &pasteboard);
2052         if (status == noErr)
2053             CFRelease(pasteboard);
2054         else
2055             QSKIP("This machine doesn't support the clipboard");
2056     }
2057 #endif
2058
2059     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2060     QDeclarativeComponent textInputComponent(&engine);
2061     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2062     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2063     QVERIFY(textInput != 0);
2064
2065     // copy and paste
2066     QCOMPARE(textInput->text().length(), 12);
2067     textInput->select(0, textInput->text().length());;
2068     textInput->copy();
2069     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2070     QCOMPARE(textInput->selectedText().length(), 12);
2071     textInput->setCursorPosition(0);
2072     QVERIFY(textInput->canPaste());
2073     textInput->paste();
2074     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2075     QCOMPARE(textInput->text().length(), 24);
2076
2077     // can paste
2078     QVERIFY(textInput->canPaste());
2079     textInput->setReadOnly(true);
2080     QVERIFY(!textInput->canPaste());
2081     textInput->setReadOnly(false);
2082     QVERIFY(textInput->canPaste());
2083
2084     // select word
2085     textInput->setCursorPosition(0);
2086     textInput->selectWord();
2087     QCOMPARE(textInput->selectedText(), QString("Hello"));
2088
2089     // select all and cut
2090     textInput->selectAll();
2091     textInput->cut();
2092     QCOMPARE(textInput->text().length(), 0);
2093     textInput->paste();
2094     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2095     QCOMPARE(textInput->text().length(), 24);
2096
2097     // clear copy buffer
2098     QClipboard *clipboard = QGuiApplication::clipboard();
2099     QVERIFY(clipboard);
2100     clipboard->clear();
2101     QVERIFY(!textInput->canPaste());
2102
2103     // test that copy functionality is disabled
2104     // when echo mode is set to hide text/password mode
2105     int index = 0;
2106     while (index < 4) {
2107         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2108         textInput->setEchoMode(echoMode);
2109         textInput->setText("My password");
2110         textInput->select(0, textInput->text().length());;
2111         textInput->copy();
2112         if (echoMode == QQuickTextInput::Normal) {
2113             QVERIFY(!clipboard->text().isEmpty());
2114             QCOMPARE(clipboard->text(), QString("My password"));
2115             clipboard->clear();
2116         } else {
2117             QVERIFY(clipboard->text().isEmpty());
2118         }
2119         index++;
2120     }
2121
2122     delete textInput;
2123 #endif
2124 }
2125
2126 void tst_qquicktextinput::copyAndPasteKeySequence() {
2127 #ifndef QT_NO_CLIPBOARD
2128
2129 #ifdef Q_OS_MAC
2130     {
2131         PasteboardRef pasteboard;
2132         OSStatus status = PasteboardCreate(0, &pasteboard);
2133         if (status == noErr)
2134             CFRelease(pasteboard);
2135         else
2136             QSKIP("This machine doesn't support the clipboard");
2137     }
2138 #endif
2139
2140     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
2141     QDeclarativeComponent textInputComponent(&engine);
2142     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2143     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2144     QVERIFY(textInput != 0);
2145
2146     QQuickCanvas canvas;
2147     textInput->setParentItem(canvas.rootItem());
2148     canvas.show();
2149     canvas.requestActivateWindow();
2150     QTest::qWaitForWindowShown(&canvas);
2151     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2152
2153     // copy and paste
2154     QVERIFY(textInput->hasActiveFocus());
2155     QCOMPARE(textInput->text().length(), 12);
2156     textInput->select(0, textInput->text().length());
2157     simulateKeys(&canvas, QKeySequence::Copy);
2158     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
2159     QCOMPARE(textInput->selectedText().length(), 12);
2160     textInput->setCursorPosition(0);
2161     QVERIFY(textInput->canPaste());
2162     simulateKeys(&canvas, QKeySequence::Paste);
2163     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2164     QCOMPARE(textInput->text().length(), 24);
2165
2166     // select all and cut
2167     simulateKeys(&canvas, QKeySequence::SelectAll);
2168     simulateKeys(&canvas, QKeySequence::Cut);
2169     QCOMPARE(textInput->text().length(), 0);
2170     simulateKeys(&canvas, QKeySequence::Paste);
2171     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
2172     QCOMPARE(textInput->text().length(), 24);
2173
2174     // clear copy buffer
2175     QClipboard *clipboard = QGuiApplication::clipboard();
2176     QVERIFY(clipboard);
2177     clipboard->clear();
2178     QVERIFY(!textInput->canPaste());
2179
2180     // test that copy functionality is disabled
2181     // when echo mode is set to hide text/password mode
2182     int index = 0;
2183     while (index < 4) {
2184         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
2185         textInput->setEchoMode(echoMode);
2186         textInput->setText("My password");
2187         textInput->select(0, textInput->text().length());;
2188         simulateKeys(&canvas, QKeySequence::Copy);
2189         if (echoMode == QQuickTextInput::Normal) {
2190             QVERIFY(!clipboard->text().isEmpty());
2191             QCOMPARE(clipboard->text(), QString("My password"));
2192             clipboard->clear();
2193         } else {
2194             QVERIFY(clipboard->text().isEmpty());
2195         }
2196         index++;
2197     }
2198
2199     delete textInput;
2200 #endif
2201 }
2202
2203 void tst_qquicktextinput::canPasteEmpty() {
2204 #ifndef QT_NO_CLIPBOARD
2205
2206     QGuiApplication::clipboard()->clear();
2207
2208     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2209     QDeclarativeComponent textInputComponent(&engine);
2210     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2211     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2212     QVERIFY(textInput != 0);
2213
2214     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2215     QCOMPARE(textInput->canPaste(), cp);
2216
2217 #endif
2218 }
2219
2220 void tst_qquicktextinput::canPaste() {
2221 #ifndef QT_NO_CLIPBOARD
2222
2223     QGuiApplication::clipboard()->setText("Some text");
2224
2225     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
2226     QDeclarativeComponent textInputComponent(&engine);
2227     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2228     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2229     QVERIFY(textInput != 0);
2230
2231     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
2232     QCOMPARE(textInput->canPaste(), cp);
2233
2234 #endif
2235 }
2236
2237 void tst_qquicktextinput::passwordCharacter()
2238 {
2239     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
2240     QDeclarativeComponent textInputComponent(&engine);
2241     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2242     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2243     QVERIFY(textInput != 0);
2244
2245     textInput->setPasswordCharacter("X");
2246     qreal implicitWidth = textInput->implicitWidth();
2247     textInput->setPasswordCharacter(".");
2248
2249     // QTBUG-12383 content is updated and redrawn
2250     QVERIFY(textInput->implicitWidth() < implicitWidth);
2251
2252     delete textInput;
2253 }
2254
2255 void tst_qquicktextinput::cursorDelegate()
2256 {
2257     QQuickView view(testFileUrl("cursorTest.qml"));
2258     view.show();
2259     view.requestActivateWindow();
2260     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
2261     QVERIFY(textInputObject != 0);
2262     QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
2263     //Test Delegate gets created
2264     textInputObject->setFocus(true);
2265     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
2266     QVERIFY(delegateObject);
2267     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2268     //Test Delegate gets moved
2269     for (int i=0; i<= textInputObject->text().length(); i++) {
2270         textInputObject->setCursorPosition(i);
2271         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2272         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2273     }
2274     textInputObject->setCursorPosition(0);
2275     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
2276     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
2277     //Test Delegate gets deleted
2278     textInputObject->setCursorDelegate(0);
2279     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
2280 }
2281
2282 void tst_qquicktextinput::cursorVisible()
2283 {
2284     QQuickView view(testFileUrl("cursorVisible.qml"));
2285     view.show();
2286     view.requestActivateWindow();
2287     QTest::qWaitForWindowShown(&view);
2288     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2289
2290     QQuickTextInput input;
2291     input.componentComplete();
2292     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
2293
2294     QCOMPARE(input.isCursorVisible(), false);
2295
2296     input.setCursorVisible(true);
2297     QCOMPARE(input.isCursorVisible(), true);
2298     QCOMPARE(spy.count(), 1);
2299
2300     input.setCursorVisible(false);
2301     QCOMPARE(input.isCursorVisible(), false);
2302     QCOMPARE(spy.count(), 2);
2303
2304     input.setFocus(true);
2305     QCOMPARE(input.isCursorVisible(), false);
2306     QCOMPARE(spy.count(), 2);
2307
2308     input.setParentItem(view.rootObject());
2309     QCOMPARE(input.isCursorVisible(), true);
2310     QCOMPARE(spy.count(), 3);
2311
2312     input.setFocus(false);
2313     QCOMPARE(input.isCursorVisible(), false);
2314     QCOMPARE(spy.count(), 4);
2315
2316     input.setFocus(true);
2317     QCOMPARE(input.isCursorVisible(), true);
2318     QCOMPARE(spy.count(), 5);
2319
2320     QQuickView alternateView;
2321     alternateView.show();
2322     alternateView.requestActivateWindow();
2323     QTest::qWaitForWindowShown(&alternateView);
2324
2325     QCOMPARE(input.isCursorVisible(), false);
2326     QCOMPARE(spy.count(), 6);
2327
2328     view.requestActivateWindow();
2329     QTest::qWaitForWindowShown(&view);
2330     QCOMPARE(input.isCursorVisible(), true);
2331     QCOMPARE(spy.count(), 7);
2332 }
2333
2334 void tst_qquicktextinput::cursorRectangle()
2335 {
2336
2337     QString text = "Hello World!";
2338
2339     QQuickTextInput input;
2340     input.setText(text);
2341     input.componentComplete();
2342
2343     QTextLayout layout(text);
2344     layout.setFont(input.font());
2345     if (!qmlDisableDistanceField()) {
2346         QTextOption option;
2347         option.setUseDesignMetrics(true);
2348         layout.setTextOption(option);
2349     }
2350     layout.beginLayout();
2351     QTextLine line = layout.createLine();
2352     layout.endLayout();
2353
2354     input.setWidth(line.cursorToX(5, QTextLine::Leading));
2355     input.setHeight(qCeil(line.height() * 3 / 2));
2356
2357     QRect r;
2358
2359     // some tolerance for different fonts.
2360 #ifdef Q_OS_LINUX
2361     const int error = 2;
2362 #else
2363     const int error = 5;
2364 #endif
2365
2366     for (int i = 0; i <= 5; ++i) {
2367         input.setCursorPosition(i);
2368         r = input.cursorRectangle();
2369
2370         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2371         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2372         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2373     }
2374
2375     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2376     QVERIFY(r.left() < input.width());
2377     QVERIFY(r.right() >= input.width() - error);
2378
2379     for (int i = 6; i < text.length(); ++i) {
2380         input.setCursorPosition(i);
2381         QCOMPARE(r, input.cursorRectangle());
2382         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2383     }
2384
2385     for (int i = text.length() - 2; i >= 0; --i) {
2386         input.setCursorPosition(i);
2387         r = input.cursorRectangle();
2388         QCOMPARE(r.top(), 0);
2389         QVERIFY(r.right() >= 0);
2390         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2391     }
2392
2393     // Check vertical scrolling with word wrap.
2394     input.setWrapMode(QQuickTextInput::WordWrap);
2395     for (int i = 0; i <= 5; ++i) {
2396         input.setCursorPosition(i);
2397         r = input.cursorRectangle();
2398
2399         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2400         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2401         QCOMPARE(r.top(), 0);
2402         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2403     }
2404
2405     input.setCursorPosition(6);
2406     r = input.cursorRectangle();
2407     QCOMPARE(r.left(), 0);
2408     QVERIFY(r.bottom() >= input.height() - error);
2409
2410     for (int i = 7; i < text.length(); ++i) {
2411         input.setCursorPosition(i);
2412         r = input.cursorRectangle();
2413         QVERIFY(r.bottom() >= input.height() - error);
2414     }
2415
2416     for (int i = text.length() - 2; i >= 6; --i) {
2417         input.setCursorPosition(i);
2418         r = input.cursorRectangle();
2419         QVERIFY(r.bottom() >= input.height() - error);
2420     }
2421
2422     for (int i = 5; i >= 0; --i) {
2423         input.setCursorPosition(i);
2424         r = input.cursorRectangle();
2425         QCOMPARE(r.top(), 0);
2426     }
2427
2428     input.setText("Hi!");
2429     input.setHAlign(QQuickTextInput::AlignRight);
2430     r = input.cursorRectangle();
2431     QVERIFY(r.left() < input.width() + error);
2432     QVERIFY(r.right() >= input.width() - error);
2433 }
2434
2435 void tst_qquicktextinput::readOnly()
2436 {
2437     QQuickView canvas(testFileUrl("readOnly.qml"));
2438     canvas.show();
2439     canvas.requestActivateWindow();
2440
2441     QVERIFY(canvas.rootObject() != 0);
2442
2443     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2444
2445     QVERIFY(input != 0);
2446     QTRY_VERIFY(input->hasActiveFocus() == true);
2447     QVERIFY(input->isReadOnly() == true);
2448     QString initial = input->text();
2449     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2450         simulateKey(&canvas, k);
2451     simulateKey(&canvas, Qt::Key_Return);
2452     simulateKey(&canvas, Qt::Key_Space);
2453     simulateKey(&canvas, Qt::Key_Escape);
2454     QCOMPARE(input->text(), initial);
2455
2456     input->setCursorPosition(3);
2457     input->setReadOnly(false);
2458     QCOMPARE(input->isReadOnly(), false);
2459     QCOMPARE(input->cursorPosition(), input->text().length());
2460 }
2461
2462 void tst_qquicktextinput::echoMode()
2463 {
2464     QQuickView canvas(testFileUrl("echoMode.qml"));
2465     canvas.show();
2466     canvas.requestActivateWindow();
2467     QTest::qWaitForWindowShown(&canvas);
2468     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2469
2470     QVERIFY(canvas.rootObject() != 0);
2471
2472     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2473
2474     QVERIFY(input != 0);
2475     QTRY_VERIFY(input->hasActiveFocus() == true);
2476     QString initial = input->text();
2477     Qt::InputMethodHints ref;
2478     QCOMPARE(initial, QLatin1String("ABCDefgh"));
2479     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2480     QCOMPARE(input->displayText(), input->text());
2481     //Normal
2482     ref &= ~Qt::ImhHiddenText;
2483     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2484     QCOMPARE(input->inputMethodHints(), ref);
2485     input->setEchoMode(QQuickTextInput::NoEcho);
2486     QCOMPARE(input->text(), initial);
2487     QCOMPARE(input->displayText(), QLatin1String(""));
2488     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2489     //NoEcho
2490     ref |= Qt::ImhHiddenText;
2491     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2492     QCOMPARE(input->inputMethodHints(), ref);
2493     input->setEchoMode(QQuickTextInput::Password);
2494     //Password
2495     ref |= Qt::ImhHiddenText;
2496     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2497     QCOMPARE(input->text(), initial);
2498     QCOMPARE(input->displayText(), QLatin1String("********"));
2499     QCOMPARE(input->inputMethodHints(), ref);
2500     input->setPasswordCharacter(QChar('Q'));
2501     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2502     QCOMPARE(input->text(), initial);
2503     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2504     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2505     //PasswordEchoOnEdit
2506     ref &= ~Qt::ImhHiddenText;
2507     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2508     QCOMPARE(input->inputMethodHints(), ref);
2509     QCOMPARE(input->text(), initial);
2510     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2511     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2512     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2513     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2514     QCOMPARE(input->text(), QLatin1String("a"));
2515     QCOMPARE(input->displayText(), QLatin1String("a"));
2516     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2517     input->setFocus(false);
2518     QVERIFY(input->hasActiveFocus() == false);
2519     QCOMPARE(input->displayText(), QLatin1String("Q"));
2520     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2521     input->setFocus(true);
2522     QVERIFY(input->hasActiveFocus());
2523     QInputMethodEvent inputEvent;
2524     inputEvent.setCommitString(initial);
2525     QGuiApplication::sendEvent(input, &inputEvent);
2526     QCOMPARE(input->text(), initial);
2527     QCOMPARE(input->displayText(), initial);
2528     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2529 }
2530
2531 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2532 void tst_qquicktextinput::passwordEchoDelay()
2533 {
2534     QQuickView canvas(testFileUrl("echoMode.qml"));
2535     canvas.show();
2536     canvas.requestActivateWindow();
2537     QTest::qWaitForWindowShown(&canvas);
2538     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2539
2540     QVERIFY(canvas.rootObject() != 0);
2541
2542     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2543
2544     QChar fillChar = QLatin1Char('*');
2545
2546     input->setEchoMode(QQuickTextInput::Password);
2547     QCOMPARE(input->displayText(), QString(8, fillChar));
2548     input->setText(QString());
2549     QCOMPARE(input->displayText(), QString());
2550
2551     QTest::keyPress(&canvas, '0');
2552     QTest::keyPress(&canvas, '1');
2553     QTest::keyPress(&canvas, '2');
2554     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2555     QTest::keyPress(&canvas, '3');
2556     QTest::keyPress(&canvas, '4');
2557     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2558     QTest::keyPress(&canvas, Qt::Key_Backspace);
2559     QCOMPARE(input->displayText(), QString(4, fillChar));
2560     QTest::keyPress(&canvas, '4');
2561     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2562     QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2563     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2564     QTest::keyPress(&canvas, '5');
2565     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2566     input->setFocus(false);
2567     QVERIFY(!input->hasFocus());
2568     QCOMPARE(input->displayText(), QString(6, fillChar));
2569     input->setFocus(true);
2570     QTRY_VERIFY(input->hasFocus());
2571     QCOMPARE(input->displayText(), QString(6, fillChar));
2572     QTest::keyPress(&canvas, '6');
2573     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2574
2575     QInputMethodEvent ev;
2576     ev.setCommitString(QLatin1String("7"));
2577     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2578     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2579
2580     input->setCursorPosition(3);
2581     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2582     QTest::keyPress(&canvas, 'a');
2583     QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2584     QTest::keyPress(&canvas, Qt::Key_Backspace);
2585     QCOMPARE(input->displayText(), QString(8, fillChar));
2586 }
2587 #endif
2588
2589
2590 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2591 {
2592     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2593     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2594
2595     QGuiApplication::sendEvent(view, &press);
2596     QGuiApplication::sendEvent(view, &release);
2597 }
2598
2599
2600 void tst_qquicktextinput::openInputPanel()
2601 {
2602     PlatformInputContext platformInputContext;
2603     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2604     inputPanelPrivate->testContext = &platformInputContext;
2605
2606     QQuickView view(testFileUrl("openInputPanel.qml"));
2607     view.show();
2608     view.requestActivateWindow();
2609     QTest::qWaitForWindowShown(&view);
2610     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2611
2612     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2613     QVERIFY(input);
2614
2615     // check default values
2616     QVERIFY(input->focusOnPress());
2617     QVERIFY(!input->hasActiveFocus());
2618     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2619     QCOMPARE(qApp->inputPanel()->visible(), false);
2620
2621     // input panel should open on focus
2622     QPoint centerPoint(view.width()/2, view.height()/2);
2623     Qt::KeyboardModifiers noModifiers = 0;
2624     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2625     QGuiApplication::processEvents();
2626     QVERIFY(input->hasActiveFocus());
2627     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2628     QCOMPARE(qApp->inputPanel()->visible(), true);
2629     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2630
2631     // input panel should be re-opened when pressing already focused TextInput
2632     qApp->inputPanel()->hide();
2633     QCOMPARE(qApp->inputPanel()->visible(), false);
2634     QVERIFY(input->hasActiveFocus());
2635     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2636     QGuiApplication::processEvents();
2637     QCOMPARE(qApp->inputPanel()->visible(), true);
2638     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2639
2640     // input panel should stay visible if focus is lost to another text inputor
2641     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2642     QQuickTextInput anotherInput;
2643     anotherInput.componentComplete();
2644     anotherInput.setParentItem(view.rootObject());
2645     anotherInput.setFocus(true);
2646     QCOMPARE(qApp->inputPanel()->visible(), true);
2647     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2648     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2649
2650     anotherInput.setFocus(false);
2651     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2652     QCOMPARE(view.activeFocusItem(), view.rootItem());
2653     anotherInput.setFocus(true);
2654
2655     // input item should be null if focus is lost to an item that doesn't accept inputs
2656     QQuickItem item;
2657     item.setParentItem(view.rootObject());
2658     item.setFocus(true);
2659     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2660     QCOMPARE(view.activeFocusItem(), &item);
2661
2662     qApp->inputPanel()->hide();
2663
2664     // input panel should not be opened if TextInput is read only
2665     input->setReadOnly(true);
2666     input->setFocus(true);
2667     QCOMPARE(qApp->inputPanel()->visible(), false);
2668     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2669     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2670     QGuiApplication::processEvents();
2671     QCOMPARE(qApp->inputPanel()->visible(), false);
2672
2673     // input panel should not be opened if focusOnPress is set to false
2674     input->setFocusOnPress(false);
2675     input->setFocus(false);
2676     input->setFocus(true);
2677     QCOMPARE(qApp->inputPanel()->visible(), false);
2678     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2679     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2680     QCOMPARE(qApp->inputPanel()->visible(), false);
2681
2682     // input panel should open when openSoftwareInputPanel is called
2683     input->openSoftwareInputPanel();
2684     QCOMPARE(qApp->inputPanel()->visible(), true);
2685
2686     // input panel should close when closeSoftwareInputPanel is called
2687     input->closeSoftwareInputPanel();
2688     QCOMPARE(qApp->inputPanel()->visible(), false);
2689 }
2690
2691 class MyTextInput : public QQuickTextInput
2692 {
2693 public:
2694     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2695     {
2696         nbPaint = 0;
2697     }
2698     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2699     {
2700        nbPaint++;
2701        return QQuickTextInput::updatePaintNode(node, data);
2702     }
2703     int nbPaint;
2704 };
2705
2706 void tst_qquicktextinput::setHAlignClearCache()
2707 {
2708     QQuickView view;
2709     MyTextInput input;
2710     input.setText("Hello world");
2711     input.setParentItem(view.rootItem());
2712     view.show();
2713     view.requestActivateWindow();
2714     QTest::qWaitForWindowShown(&view);
2715 #ifdef Q_OS_MAC
2716     QEXPECT_FAIL("", "QTBUG-23485", Abort);
2717 #endif
2718     QTRY_COMPARE(input.nbPaint, 1);
2719     input.setHAlign(QQuickTextInput::AlignRight);
2720     //Changing the alignment should trigger a repaint
2721     QTRY_COMPARE(input.nbPaint, 2);
2722 }
2723
2724 void tst_qquicktextinput::focusOutClearSelection()
2725 {
2726     QQuickView view;
2727     QQuickTextInput input;
2728     QQuickTextInput input2;
2729     input.setText(QLatin1String("Hello world"));
2730     input.setFocus(true);
2731     input2.setParentItem(view.rootItem());
2732     input.setParentItem(view.rootItem());
2733     input.componentComplete();
2734     input2.componentComplete();
2735     view.show();
2736     view.requestActivateWindow();
2737     QTest::qWaitForWindowShown(&view);
2738     input.select(2,5);
2739     //The selection should work
2740     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2741     input2.setFocus(true);
2742     QGuiApplication::processEvents();
2743     //The input lost the focus selection should be cleared
2744     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2745 }
2746
2747 void tst_qquicktextinput::geometrySignals()
2748 {
2749     QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2750     QObject *o = component.create();
2751     QVERIFY(o);
2752     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2753     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2754     delete o;
2755 }
2756
2757 void tst_qquicktextinput::testQtQuick11Attributes()
2758 {
2759     QFETCH(QString, code);
2760     QFETCH(QString, warning);
2761     QFETCH(QString, error);
2762
2763     QDeclarativeEngine engine;
2764     QObject *obj;
2765
2766     QDeclarativeComponent valid(&engine);
2767     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2768     obj = valid.create();
2769     QVERIFY(obj);
2770     QVERIFY(valid.errorString().isEmpty());
2771     delete obj;
2772
2773     QDeclarativeComponent invalid(&engine);
2774     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2775     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2776     obj = invalid.create();
2777     QCOMPARE(invalid.errorString(), error);
2778     delete obj;
2779 }
2780
2781 void tst_qquicktextinput::testQtQuick11Attributes_data()
2782 {
2783     QTest::addColumn<QString>("code");
2784     QTest::addColumn<QString>("warning");
2785     QTest::addColumn<QString>("error");
2786
2787     QTest::newRow("canPaste") << "property bool foo: canPaste"
2788         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2789         << "";
2790
2791     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2792         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2793         << "";
2794
2795     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2796         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2797         << "";
2798 }
2799
2800 static void sendPreeditText(const QString &text, int cursor)
2801 {
2802     QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2803             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2804     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2805 }
2806
2807 void tst_qquicktextinput::preeditAutoScroll()
2808 {
2809     QString preeditText = "califragisiticexpialidocious!";
2810
2811     QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2812     view.show();
2813     view.requestActivateWindow();
2814     QTest::qWaitForWindowShown(&view);
2815     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2816     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2817     QVERIFY(input);
2818     QVERIFY(input->hasActiveFocus());
2819
2820     input->setWidth(input->implicitWidth());
2821
2822     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2823     int cursorRectangleChanges = 0;
2824
2825     // test the text is scrolled so the preedit is visible.
2826     sendPreeditText(preeditText.mid(0, 3), 1);
2827     QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2828     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2829     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2830
2831     // test the text is scrolled back when the preedit is removed.
2832     QInputMethodEvent imEvent;
2833     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2834     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2835     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2836     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2837
2838     QTextLayout layout(preeditText);
2839     layout.setFont(input->font());
2840     if (!qmlDisableDistanceField()) {
2841         QTextOption option;
2842         option.setUseDesignMetrics(true);
2843         layout.setTextOption(option);
2844     }
2845     layout.beginLayout();
2846     QTextLine line = layout.createLine();
2847     layout.endLayout();
2848
2849     // test if the preedit is larger than the text input that the
2850     // character preceding the cursor is still visible.
2851     qreal x = input->positionToRectangle(0).x();
2852     for (int i = 0; i < 3; ++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     for (int i = 1; i >= 0; --i) {
2861         sendPreeditText(preeditText, i + 1);
2862         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2863         QVERIFY(input->cursorRectangle().right() >= width - 3);
2864         QVERIFY(input->positionToRectangle(0).x() > x);
2865         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2866         x = input->positionToRectangle(0).x();
2867     }
2868
2869     // Test incrementing the preedit cursor doesn't cause further
2870     // scrolling when right most text is visible.
2871     sendPreeditText(preeditText, preeditText.length() - 3);
2872     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2873     x = input->positionToRectangle(0).x();
2874     for (int i = 2; i >= 0; --i) {
2875         sendPreeditText(preeditText, preeditText.length() - i);
2876         QCOMPARE(input->positionToRectangle(0).x(), x);
2877         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2878     }
2879     for (int i = 1; i <  3; ++i) {
2880         sendPreeditText(preeditText, preeditText.length() - i);
2881         QCOMPARE(input->positionToRectangle(0).x(), x);
2882         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2883     }
2884
2885     // Test disabling auto scroll.
2886     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2887
2888     input->setAutoScroll(false);
2889     sendPreeditText(preeditText.mid(0, 3), 1);
2890     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2891     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2892 }
2893
2894 void tst_qquicktextinput::preeditCursorRectangle()
2895 {
2896     QString preeditText = "super";
2897
2898     QQuickView view(testFileUrl("inputMethodEvent.qml"));
2899     view.show();
2900     view.requestActivateWindow();
2901     QTest::qWaitForWindowShown(&view);
2902     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2903     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2904     QVERIFY(input);
2905
2906     QRect currentRect;
2907
2908     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2909     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2910     QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2911
2912     // Verify that the micro focus rect is positioned the same for position 0 as
2913     // it would be if there was no preedit text.
2914     sendPreeditText(preeditText, 0);
2915     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2916     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2917     QCOMPARE(currentRect, previousRect);
2918
2919     QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2920     QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2921
2922     // Verify that the micro focus rect moves to the left as the cursor position
2923     // is incremented.
2924     for (int i = 1; i <= 5; ++i) {
2925         sendPreeditText(preeditText, i);
2926         QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2927         currentRect = query.value(Qt::ImCursorRectangle).toRect();
2928         QVERIFY(previousRect.left() < currentRect.left());
2929         QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2930         QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2931         previousRect = currentRect;
2932     }
2933
2934     // Verify that if there is no preedit cursor then the micro focus rect is the
2935     // same as it would be if it were positioned at the end of the preedit text.
2936     sendPreeditText(preeditText, 0);
2937     QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2938     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2939     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2940     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2941     QCOMPARE(currentRect, previousRect);
2942     QVERIFY(inputSpy.count() > 0);
2943     QVERIFY(panelSpy.count() > 0);
2944 }
2945
2946 void tst_qquicktextinput::inputContextMouseHandler()
2947 {
2948     PlatformInputContext platformInputContext;
2949     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2950     inputPanelPrivate->testContext = &platformInputContext;
2951
2952     QString text = "supercalifragisiticexpialidocious!";
2953     QQuickView view(testFileUrl("inputContext.qml"));
2954     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2955     QVERIFY(input);
2956
2957     input->setFocus(true);
2958     input->setText("");
2959
2960     view.show();
2961     view.requestActivateWindow();
2962     QTest::qWaitForWindowShown(&view);
2963     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2964
2965     QTextLayout layout(text);
2966     layout.setFont(input->font());
2967     if (!qmlDisableDistanceField()) {
2968         QTextOption option;
2969         option.setUseDesignMetrics(true);
2970         layout.setTextOption(option);
2971     }
2972     layout.beginLayout();
2973     QTextLine line = layout.createLine();
2974     layout.endLayout();
2975
2976     const qreal x = line.cursorToX(2, QTextLine::Leading);
2977     const qreal y = line.height() / 2;
2978     QPoint position = QPointF(x, y).toPoint();
2979
2980     QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2981     QGuiApplication::sendEvent(input, &inputEvent);
2982
2983     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2984     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2985     QGuiApplication::processEvents();
2986
2987     QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2988     QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2989     QCOMPARE(platformInputContext.m_cursorPosition, 2);
2990 }
2991
2992 void tst_qquicktextinput::inputMethodComposing()
2993 {
2994     QString text = "supercalifragisiticexpialidocious!";
2995
2996     QQuickView view(testFileUrl("inputContext.qml"));
2997     view.show();
2998     view.requestActivateWindow();
2999     QTest::qWaitForWindowShown(&view);
3000     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3001     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3002     QVERIFY(input);
3003     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
3004
3005     QCOMPARE(input->isInputMethodComposing(), false);
3006     {
3007         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3008         QGuiApplication::sendEvent(input, &event);
3009     }
3010     QCOMPARE(input->isInputMethodComposing(), true);
3011     QCOMPARE(spy.count(), 1);
3012
3013     {
3014         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3015         QGuiApplication::sendEvent(input, &event);
3016     }
3017     QCOMPARE(spy.count(), 1);
3018
3019     {
3020         QInputMethodEvent event;
3021         QGuiApplication::sendEvent(input, &event);
3022     }
3023     QCOMPARE(input->isInputMethodComposing(), false);
3024     QCOMPARE(spy.count(), 2);
3025 }
3026
3027 void tst_qquicktextinput::inputPanelUpdate()
3028 {
3029     PlatformInputContext platformInputContext;
3030     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
3031     inputPanelPrivate->testContext = &platformInputContext;
3032
3033     QQuickView view(testFileUrl("inputContext.qml"));
3034     view.show();
3035     view.requestActivateWindow();
3036     QTest::qWaitForWindowShown(&view);
3037     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3038     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
3039     QVERIFY(input);
3040
3041     // text change even without cursor position change needs to trigger update
3042     input->setText("test");
3043     platformInputContext.clear();
3044     input->setText("xxxx");
3045     QVERIFY(platformInputContext.m_updateCallCount > 0);
3046
3047     // input method event replacing text
3048     platformInputContext.clear();
3049     {
3050         QInputMethodEvent inputMethodEvent;
3051         inputMethodEvent.setCommitString("y", -1, 1);
3052         QGuiApplication::sendEvent(input, &inputMethodEvent);
3053     }
3054     QVERIFY(platformInputContext.m_updateCallCount > 0);
3055
3056     // input method changing selection
3057     platformInputContext.clear();
3058     {
3059         QList<QInputMethodEvent::Attribute> attributes;
3060         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3061         QInputMethodEvent inputMethodEvent("", attributes);
3062         QGuiApplication::sendEvent(input, &inputMethodEvent);
3063     }
3064     QVERIFY(input->selectionStart() != input->selectionEnd());
3065     QVERIFY(platformInputContext.m_updateCallCount > 0);
3066
3067     // programmatical selections trigger update
3068     platformInputContext.clear();
3069     input->selectAll();
3070     QVERIFY(platformInputContext.m_updateCallCount > 0);
3071
3072     // font changes
3073     platformInputContext.clear();
3074     QFont font = input->font();
3075     font.setBold(!font.bold());
3076     input->setFont(font);
3077     QVERIFY(platformInputContext.m_updateCallCount > 0);
3078
3079     // normal input
3080     platformInputContext.clear();
3081     {
3082         QInputMethodEvent inputMethodEvent;
3083         inputMethodEvent.setCommitString("y");
3084         QGuiApplication::sendEvent(input, &inputMethodEvent);
3085     }
3086     QVERIFY(platformInputContext.m_updateCallCount > 0);
3087
3088     // changing cursor position
3089     platformInputContext.clear();
3090     input->setCursorPosition(0);
3091     QVERIFY(platformInputContext.m_updateCallCount > 0);
3092
3093     // read only disabled input method
3094     platformInputContext.clear();
3095     input->setReadOnly(true);
3096     QVERIFY(platformInputContext.m_updateCallCount > 0);
3097     input->setReadOnly(false);
3098
3099     // no updates while no focus
3100     input->setFocus(false);
3101     platformInputContext.clear();
3102     input->setText("Foo");
3103     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3104     input->setCursorPosition(1);
3105     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3106     input->selectAll();
3107     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3108     input->setReadOnly(true);
3109     QCOMPARE(platformInputContext.m_updateCallCount, 0);
3110 }
3111
3112 void tst_qquicktextinput::cursorRectangleSize()
3113 {
3114     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3115     QVERIFY(canvas->rootObject() != 0);
3116     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
3117
3118     // make sure cursor rectangle is not at (0,0)
3119     textInput->setX(10);
3120     textInput->setY(10);
3121     textInput->setCursorPosition(3);
3122     QVERIFY(textInput != 0);
3123     textInput->setFocus(true);
3124     canvas->show();
3125     canvas->requestActivateWindow();
3126     QTest::qWaitForWindowShown(canvas);
3127
3128     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3129     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
3130     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3131
3132     QRect cursorRectFromItem = textInput->cursorRectangle();
3133     QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
3134
3135     // item and input query cursor rectangles match
3136     QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
3137
3138     // item cursor rectangle and positionToRectangle calculations match
3139     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
3140
3141     // item-canvas transform and input item transform match
3142     QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
3143
3144     // input panel cursorRectangle property and tranformed item cursor rectangle match
3145     QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3146     QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
3147
3148     delete canvas;
3149 }
3150
3151 void tst_qquicktextinput::tripleClickSelectsAll()
3152 {
3153     QString qmlfile = testFile("positionAt.qml");
3154     QQuickView view(QUrl::fromLocalFile(qmlfile));
3155     view.show();
3156     view.requestActivateWindow();
3157     QTest::qWaitForWindowShown(&view);
3158
3159     QTRY_COMPARE(&view, qGuiApp->focusWindow());
3160
3161     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
3162     QVERIFY(input);
3163
3164     QLatin1String hello("Hello world!");
3165     input->setSelectByMouse(true);
3166     input->setText(hello);
3167
3168     // Clicking on the same point inside TextInput three times in a row
3169     // should trigger a triple click, thus selecting all the text.
3170     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
3171     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3172     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3173     QGuiApplication::processEvents();
3174     QCOMPARE(input->selectedText(), hello);
3175
3176     // Now it simulates user moving the mouse between the second and the third click.
3177     // In this situation, we don't expect a triple click.
3178     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
3179     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3180     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
3181     QGuiApplication::processEvents();
3182     QVERIFY(input->selectedText().isEmpty());
3183
3184     // And now we press the third click too late, so no triple click event is triggered.
3185     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
3186     QGuiApplication::processEvents();
3187     QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
3188     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
3189     QGuiApplication::processEvents();
3190     QVERIFY(input->selectedText().isEmpty());
3191 }
3192
3193 void tst_qquicktextinput::QTBUG_19956_data()
3194 {
3195     QTest::addColumn<QString>("url");
3196     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
3197     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
3198 }
3199
3200
3201 void tst_qquicktextinput::getText_data()
3202 {
3203     QTest::addColumn<QString>("text");
3204     QTest::addColumn<QString>("inputMask");
3205     QTest::addColumn<int>("start");
3206     QTest::addColumn<int>("end");
3207     QTest::addColumn<QString>("expectedText");
3208
3209     QTest::newRow("all plain text")
3210             << standard.at(0)
3211             << QString()
3212             << 0 << standard.at(0).length()
3213             << standard.at(0);
3214
3215     QTest::newRow("plain text sub string")
3216             << standard.at(0)
3217             << QString()
3218             << 0 << 12
3219             << standard.at(0).mid(0, 12);
3220
3221     QTest::newRow("plain text sub string reversed")
3222             << standard.at(0)
3223             << QString()
3224             << 12 << 0
3225             << standard.at(0).mid(0, 12);
3226
3227     QTest::newRow("plain text cropped beginning")
3228             << standard.at(0)
3229             << QString()
3230             << -3 << 4
3231             << standard.at(0).mid(0, 4);
3232
3233     QTest::newRow("plain text cropped end")
3234             << standard.at(0)
3235             << QString()
3236             << 23 << standard.at(0).length() + 8
3237             << standard.at(0).mid(23);
3238
3239     QTest::newRow("plain text cropped beginning and end")
3240             << standard.at(0)
3241             << QString()
3242             << -9 << standard.at(0).length() + 4
3243             << standard.at(0);
3244 }
3245
3246 void tst_qquicktextinput::getText()
3247 {
3248     QFETCH(QString, text);
3249     QFETCH(QString, inputMask);
3250     QFETCH(int, start);
3251     QFETCH(int, end);
3252     QFETCH(QString, expectedText);
3253
3254     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3255     QDeclarativeComponent textInputComponent(&engine);
3256     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3257     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3258     QVERIFY(textInput != 0);
3259
3260     QCOMPARE(textInput->getText(start, end), expectedText);
3261 }
3262
3263 void tst_qquicktextinput::insert_data()
3264 {
3265     QTest::addColumn<QString>("text");
3266     QTest::addColumn<QString>("inputMask");
3267     QTest::addColumn<int>("selectionStart");
3268     QTest::addColumn<int>("selectionEnd");
3269     QTest::addColumn<int>("insertPosition");
3270     QTest::addColumn<QString>("insertText");
3271     QTest::addColumn<QString>("expectedText");
3272     QTest::addColumn<int>("expectedSelectionStart");
3273     QTest::addColumn<int>("expectedSelectionEnd");
3274     QTest::addColumn<int>("expectedCursorPosition");
3275     QTest::addColumn<bool>("selectionChanged");
3276     QTest::addColumn<bool>("cursorPositionChanged");
3277
3278     QTest::newRow("at cursor position (beginning)")
3279             << standard.at(0)
3280             << QString()
3281             << 0 << 0 << 0
3282             << QString("Hello")
3283             << QString("Hello") + standard.at(0)
3284             << 5 << 5 << 5
3285             << false << true;
3286
3287     QTest::newRow("at cursor position (end)")
3288             << standard.at(0)
3289             << QString()
3290             << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3291             << QString("Hello")
3292             << standard.at(0) + QString("Hello")
3293             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3294             << false << true;
3295
3296     QTest::newRow("at cursor position (middle)")
3297             << standard.at(0)
3298             << QString()
3299             << 18 << 18 << 18
3300             << QString("Hello")
3301             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3302             << 23 << 23 << 23
3303             << false << true;
3304
3305     QTest::newRow("after cursor position (beginning)")
3306             << standard.at(0)
3307             << QString()
3308             << 0 << 0 << 18
3309             << QString("Hello")
3310             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3311             << 0 << 0 << 0
3312             << false << false;
3313
3314     QTest::newRow("before cursor position (end)")
3315             << standard.at(0)
3316             << QString()
3317             << standard.at(0).length() << standard.at(0).length() << 18
3318             << QString("Hello")
3319             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3320             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3321             << false << true;
3322
3323     QTest::newRow("before cursor position (middle)")
3324             << standard.at(0)
3325             << QString()
3326             << 18 << 18 << 0
3327             << QString("Hello")
3328             << QString("Hello") + standard.at(0)
3329             << 23 << 23 << 23
3330             << false << true;
3331
3332     QTest::newRow("after cursor position (middle)")
3333             << standard.at(0)
3334             << QString()
3335             << 18 << 18 << standard.at(0).length()
3336             << QString("Hello")
3337             << standard.at(0) + QString("Hello")
3338             << 18 << 18 << 18
3339             << false << false;
3340
3341     QTest::newRow("before selection")
3342             << standard.at(0)
3343             << QString()
3344             << 14 << 19 << 0
3345             << QString("Hello")
3346             << QString("Hello") + standard.at(0)
3347             << 19 << 24 << 24
3348             << false << true;
3349
3350     QTest::newRow("before reversed selection")
3351             << standard.at(0)
3352             << QString()
3353             << 19 << 14 << 0
3354             << QString("Hello")
3355             << QString("Hello") + standard.at(0)
3356             << 19 << 24 << 19
3357             << false << true;
3358
3359     QTest::newRow("after selection")
3360             << standard.at(0)
3361             << QString()
3362             << 14 << 19 << standard.at(0).length()
3363             << QString("Hello")
3364             << standard.at(0) + QString("Hello")
3365             << 14 << 19 << 19
3366             << false << false;
3367
3368     QTest::newRow("after reversed selection")
3369             << standard.at(0)
3370             << QString()
3371             << 19 << 14 << standard.at(0).length()
3372             << QString("Hello")
3373             << standard.at(0) + QString("Hello")
3374             << 14 << 19 << 14
3375             << false << false;
3376
3377     QTest::newRow("into selection")
3378             << standard.at(0)
3379             << QString()
3380             << 14 << 19 << 18
3381             << QString("Hello")
3382             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3383             << 14 << 24 << 24
3384             << true << true;
3385
3386     QTest::newRow("into reversed selection")
3387             << standard.at(0)
3388             << QString()
3389             << 19 << 14 << 18
3390             << QString("Hello")
3391             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3392             << 14 << 24 << 14
3393             << true << false;
3394
3395     QTest::newRow("rich text into plain text")
3396             << standard.at(0)
3397             << QString()
3398             << 0 << 0 << 0
3399             << QString("<b>Hello</b>")
3400             << QString("<b>Hello</b>") + standard.at(0)
3401             << 12 << 12 << 12
3402             << false << true;
3403
3404     QTest::newRow("before start")
3405             << standard.at(0)
3406             << QString()
3407             << 0 << 0 << -3
3408             << QString("Hello")
3409             << standard.at(0)
3410             << 0 << 0 << 0
3411             << false << false;
3412
3413     QTest::newRow("past end")
3414             << standard.at(0)
3415             << QString()
3416             << 0 << 0 << standard.at(0).length() + 3
3417             << QString("Hello")
3418             << standard.at(0)
3419             << 0 << 0 << 0
3420             << false << false;
3421
3422     const QString inputMask = "009.009.009.009";
3423     const QString ip = "192.168.2.14";
3424
3425     QTest::newRow("mask: at cursor position (beginning)")
3426             << ip
3427             << inputMask
3428             << 0 << 0 << 0
3429             << QString("125")
3430             << QString("125.168.2.14")
3431             << 0 << 0 << 0
3432             << false << false;
3433
3434     QTest::newRow("mask: at cursor position (end)")
3435             << ip
3436             << inputMask
3437             << inputMask.length() << inputMask.length() << inputMask.length()
3438             << QString("8")
3439             << ip
3440             << inputMask.length() << inputMask.length() << inputMask.length()
3441             << false << false;
3442
3443     QTest::newRow("mask: at cursor position (middle)")
3444             << ip
3445             << inputMask
3446             << 6 << 6 << 6
3447             << QString("75.2")
3448             << QString("192.167.5.24")
3449             << 6 << 6 << 6
3450             << false << false;
3451
3452     QTest::newRow("mask: after cursor position (beginning)")
3453             << ip
3454             << inputMask
3455             << 0 << 0 << 6
3456             << QString("75.2")
3457             << QString("192.167.5.24")
3458             << 0 << 0 << 0
3459             << false << false;
3460
3461     QTest::newRow("mask: before cursor position (end)")
3462             << ip
3463             << inputMask
3464             << inputMask.length() << inputMask.length() << 6
3465             << QString("75.2")
3466             << QString("192.167.5.24")
3467             << inputMask.length() << inputMask.length() << inputMask.length()
3468             << false << false;
3469
3470     QTest::newRow("mask: before cursor position (middle)")
3471             << ip
3472             << inputMask
3473             << 6 << 6 << 0
3474             << QString("125")
3475             << QString("125.168.2.14")
3476             << 6 << 6 << 6
3477             << false << false;
3478
3479     QTest::newRow("mask: after cursor position (middle)")
3480             << ip
3481             << inputMask
3482             << 6 << 6 << 13
3483             << QString("8")
3484             << "192.168.2.18"
3485             << 6 << 6 << 6
3486             << false << false;
3487
3488     QTest::newRow("mask: before selection")
3489             << ip
3490             << inputMask
3491             << 6 << 8 << 0
3492             << QString("125")
3493             << QString("125.168.2.14")
3494             << 6 << 8 << 8
3495             << false << false;
3496
3497     QTest::newRow("mask: before reversed selection")
3498             << ip
3499             << inputMask
3500             << 8 << 6 << 0
3501             << QString("125")
3502             << QString("125.168.2.14")
3503             << 6 << 8 << 6
3504             << false << false;
3505
3506     QTest::newRow("mask: after selection")
3507             << ip
3508             << inputMask
3509             << 6 << 8 << 13
3510             << QString("8")
3511             << "192.168.2.18"
3512             << 6 << 8 << 8
3513             << false << false;
3514
3515     QTest::newRow("mask: after reversed selection")
3516             << ip
3517             << inputMask
3518             << 8 << 6 << 13
3519             << QString("8")
3520             << "192.168.2.18"
3521             << 6 << 8 << 6
3522             << false << false;
3523
3524     QTest::newRow("mask: into selection")
3525             << ip
3526             << inputMask
3527             << 5 << 8 << 6
3528             << QString("75.2")
3529             << QString("192.167.5.24")
3530             << 5 << 8 << 8
3531             << true << false;
3532
3533     QTest::newRow("mask: into reversed selection")
3534             << ip
3535             << inputMask
3536             << 8 << 5 << 6
3537             << QString("75.2")
3538             << QString("192.167.5.24")
3539             << 5 << 8 << 5
3540             << true << false;
3541
3542     QTest::newRow("mask: before start")
3543             << ip
3544             << inputMask
3545             << 0 << 0 << -3
3546             << QString("4")
3547             << ip
3548             << 0 << 0 << 0
3549             << false << false;
3550
3551     QTest::newRow("mask: past end")
3552             << ip
3553             << inputMask
3554             << 0 << 0 << ip.length() + 3
3555             << QString("4")
3556             << ip
3557             << 0 << 0 << 0
3558             << false << false;
3559
3560     QTest::newRow("mask: invalid characters")
3561             << ip
3562             << inputMask
3563             << 0 << 0 << 0
3564             << QString("abc")
3565             << QString("192.168.2.14")
3566             << 0 << 0 << 0
3567             << false << false;
3568
3569     QTest::newRow("mask: mixed validity")
3570             << ip
3571             << inputMask
3572             << 0 << 0 << 0
3573             << QString("a1b2c5")
3574             << QString("125.168.2.14")
3575             << 0 << 0 << 0
3576             << false << false;
3577 }
3578
3579 void tst_qquicktextinput::insert()
3580 {
3581     QFETCH(QString, text);
3582     QFETCH(QString, inputMask);
3583     QFETCH(int, selectionStart);
3584     QFETCH(int, selectionEnd);
3585     QFETCH(int, insertPosition);
3586     QFETCH(QString, insertText);
3587     QFETCH(QString, expectedText);
3588     QFETCH(int, expectedSelectionStart);
3589     QFETCH(int, expectedSelectionEnd);
3590     QFETCH(int, expectedCursorPosition);
3591     QFETCH(bool, selectionChanged);
3592     QFETCH(bool, cursorPositionChanged);
3593
3594     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3595     QDeclarativeComponent textInputComponent(&engine);
3596     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3597     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3598     QVERIFY(textInput != 0);
3599
3600     textInput->select(selectionStart, selectionEnd);
3601
3602     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3603     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3604     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3605     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3606     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3607
3608     textInput->insert(insertPosition, insertText);
3609
3610     QCOMPARE(textInput->text(), expectedText);
3611     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3612
3613     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3614     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3615     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3616
3617     if (selectionStart > selectionEnd)
3618         qSwap(selectionStart, selectionEnd);
3619
3620     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3621     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3622     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3623     QCOMPARE(textSpy.count() > 0, text != expectedText);
3624     QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3625 }
3626
3627 void tst_qquicktextinput::remove_data()
3628 {
3629     QTest::addColumn<QString>("text");
3630     QTest::addColumn<QString>("inputMask");
3631     QTest::addColumn<int>("selectionStart");
3632     QTest::addColumn<int>("selectionEnd");
3633     QTest::addColumn<int>("removeStart");
3634     QTest::addColumn<int>("removeEnd");
3635     QTest::addColumn<QString>("expectedText");
3636     QTest::addColumn<int>("expectedSelectionStart");
3637     QTest::addColumn<int>("expectedSelectionEnd");
3638     QTest::addColumn<int>("expectedCursorPosition");
3639     QTest::addColumn<bool>("selectionChanged");
3640     QTest::addColumn<bool>("cursorPositionChanged");
3641
3642     QTest::newRow("from cursor position (beginning)")
3643             << standard.at(0)
3644             << QString()
3645             << 0 << 0
3646             << 0 << 5
3647             << standard.at(0).mid(5)
3648             << 0 << 0 << 0
3649             << false << false;
3650
3651     QTest::newRow("to cursor position (beginning)")
3652             << standard.at(0)
3653             << QString()
3654             << 0 << 0
3655             << 5 << 0
3656             << standard.at(0).mid(5)
3657             << 0 << 0 << 0
3658             << false << false;
3659
3660     QTest::newRow("to cursor position (end)")
3661             << standard.at(0)
3662             << QString()
3663             << standard.at(0).length() << standard.at(0).length()
3664             << standard.at(0).length() << standard.at(0).length() - 5
3665             << standard.at(0).mid(0, standard.at(0).length() - 5)
3666             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3667             << false << true;
3668
3669     QTest::newRow("to cursor position (end)")
3670             << standard.at(0)
3671             << QString()
3672             << standard.at(0).length() << standard.at(0).length()
3673             << standard.at(0).length() - 5 << standard.at(0).length()
3674             << standard.at(0).mid(0, standard.at(0).length() - 5)
3675             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3676             << false << true;
3677
3678     QTest::newRow("from cursor position (middle)")
3679             << standard.at(0)
3680             << QString()
3681             << 18 << 18
3682             << 18 << 23
3683             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3684             << 18 << 18 << 18
3685             << false << false;
3686
3687     QTest::newRow("to cursor position (middle)")
3688             << standard.at(0)
3689             << QString()
3690             << 23 << 23
3691             << 18 << 23
3692             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3693             << 18 << 18 << 18
3694             << false << true;
3695
3696     QTest::newRow("after cursor position (beginning)")
3697             << standard.at(0)
3698             << QString()
3699             << 0 << 0
3700             << 18 << 23
3701             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3702             << 0 << 0 << 0
3703             << false << false;
3704
3705     QTest::newRow("before cursor position (end)")
3706             << standard.at(0)
3707             << QString()
3708             << standard.at(0).length() << standard.at(0).length()
3709             << 18 << 23
3710             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3711             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3712             << false << true;
3713
3714     QTest::newRow("before cursor position (middle)")
3715             << standard.at(0)
3716             << QString()
3717             << 23 << 23
3718             << 0 << 5
3719             << standard.at(0).mid(5)
3720             << 18 << 18 << 18
3721             << false << true;
3722
3723     QTest::newRow("after cursor position (middle)")
3724             << standard.at(0)
3725             << QString()
3726             << 18 << 18
3727             << 18 << 23
3728             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3729             << 18 << 18 << 18
3730             << false << false;
3731
3732     QTest::newRow("before selection")
3733             << standard.at(0)
3734             << QString()
3735             << 14 << 19
3736             << 0 << 5
3737             << standard.at(0).mid(5)
3738             << 9 << 14 << 14
3739             << false << true;
3740
3741     QTest::newRow("before reversed selection")
3742             << standard.at(0)
3743             << QString()
3744             << 19 << 14
3745             << 0 << 5
3746             << standard.at(0).mid(5)
3747             << 9 << 14 << 9
3748             << false << true;
3749
3750     QTest::newRow("after selection")
3751             << standard.at(0)
3752             << QString()
3753             << 14 << 19
3754             << standard.at(0).length() - 5 << standard.at(0).length()
3755             << standard.at(0).mid(0, standard.at(0).length() - 5)
3756             << 14 << 19 << 19
3757             << false << false;
3758
3759     QTest::newRow("after reversed selection")
3760             << standard.at(0)
3761             << QString()
3762             << 19 << 14
3763             << standard.at(0).length() - 5 << standard.at(0).length()
3764             << standard.at(0).mid(0, standard.at(0).length() - 5)
3765             << 14 << 19 << 14
3766             << false << false;
3767
3768     QTest::newRow("from selection")
3769             << standard.at(0)
3770             << QString()
3771             << 14 << 24
3772             << 18 << 23
3773             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3774             << 14 << 19 << 19
3775             << true << true;
3776
3777     QTest::newRow("from reversed selection")
3778             << standard.at(0)
3779             << QString()
3780             << 24 << 14
3781             << 18 << 23
3782             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3783             << 14 << 19 << 14
3784             << true << false;
3785
3786     QTest::newRow("cropped beginning")
3787             << standard.at(0)
3788             << QString()
3789             << 0 << 0
3790             << -3 << 4
3791             << standard.at(0).mid(4)
3792             << 0 << 0 << 0
3793             << false << false;
3794
3795     QTest::newRow("cropped end")
3796             << standard.at(0)
3797             << QString()
3798             << 0 << 0
3799             << 23 << standard.at(0).length() + 8
3800             << standard.at(0).mid(0, 23)
3801             << 0 << 0 << 0
3802             << false << false;
3803
3804     QTest::newRow("cropped beginning and end")
3805             << standard.at(0)
3806             << QString()
3807             << 0 << 0
3808             << -9 << standard.at(0).length() + 4
3809             << QString()
3810             << 0 << 0 << 0
3811             << false << false;
3812
3813     const QString inputMask = "009.009.009.009";
3814     const QString ip = "192.168.2.14";
3815
3816     QTest::newRow("mask: from cursor position")
3817             << ip
3818             << inputMask
3819             << 6 << 6
3820             << 6 << 9
3821             << QString("192.16..14")
3822             << 6 << 6 << 6
3823             << false << false;
3824
3825     QTest::newRow("mask: to cursor position")
3826             << ip
3827             << inputMask
3828             << 6 << 6
3829             << 2 << 6
3830             << QString("19.8.2.14")
3831             << 6 << 6 << 6
3832             << false << false;
3833
3834     QTest::newRow("mask: before cursor position")
3835             << ip
3836             << inputMask
3837             << 6 << 6
3838             << 0 << 2
3839             << QString("2.168.2.14")
3840             << 6 << 6 << 6
3841             << false << false;
3842
3843     QTest::newRow("mask: after cursor position")
3844             << ip
3845             << inputMask
3846             << 6 << 6
3847             << 12 << 16
3848             << QString("192.168.2.")
3849             << 6 << 6 << 6
3850             << false << false;
3851
3852     QTest::newRow("mask: before selection")
3853             << ip
3854             << inputMask
3855             << 6 << 8
3856             << 0 << 2
3857             << QString("2.168.2.14")
3858             << 6 << 8 << 8
3859             << false << false;
3860
3861     QTest::newRow("mask: before reversed selection")
3862             << ip
3863             << inputMask
3864             << 8 << 6
3865             << 0 << 2
3866             << QString("2.168.2.14")
3867             << 6 << 8 << 6
3868             << false << false;
3869
3870     QTest::newRow("mask: after selection")
3871             << ip
3872             << inputMask
3873             << 6 << 8
3874             << 12 << 16
3875             << QString("192.168.2.")
3876             << 6 << 8 << 8
3877             << false << false;
3878
3879     QTest::newRow("mask: after reversed selection")
3880             << ip
3881             << inputMask
3882             << 8 << 6
3883             << 12 << 16
3884             << QString("192.168.2.")
3885             << 6 << 8 << 6
3886             << false << false;
3887
3888     QTest::newRow("mask: from selection")
3889             << ip
3890             << inputMask
3891             << 6 << 13
3892             << 8 << 10
3893             << QString("192.168..14")
3894             << 6 << 13 << 13
3895             << true << false;
3896
3897     QTest::newRow("mask: from reversed selection")
3898             << ip
3899             << inputMask
3900             << 13 << 6
3901             << 8 << 10
3902             << QString("192.168..14")
3903             << 6 << 13 << 6
3904             << true << false;
3905
3906     QTest::newRow("mask: cropped beginning")
3907             << ip
3908             << inputMask
3909             << 0 << 0
3910             << -3 << 4
3911             << QString(".168.2.14")
3912             << 0 << 0 << 0
3913             << false << false;
3914
3915     QTest::newRow("mask: cropped end")
3916             << ip
3917             << inputMask
3918             << 0 << 0
3919             << 13 << 28
3920             << QString("192.168.2.1")
3921             << 0 << 0 << 0
3922             << false << false;
3923
3924     QTest::newRow("mask: cropped beginning and end")
3925             << ip
3926             << inputMask
3927             << 0 << 0
3928             << -9 << 28
3929             << QString("...")
3930             << 0 << 0 << 0
3931             << false << false;
3932 }
3933
3934 void tst_qquicktextinput::remove()
3935 {
3936     QFETCH(QString, text);
3937     QFETCH(QString, inputMask);
3938     QFETCH(int, selectionStart);
3939     QFETCH(int, selectionEnd);
3940     QFETCH(int, removeStart);
3941     QFETCH(int, removeEnd);
3942     QFETCH(QString, expectedText);
3943     QFETCH(int, expectedSelectionStart);
3944     QFETCH(int, expectedSelectionEnd);
3945     QFETCH(int, expectedCursorPosition);
3946     QFETCH(bool, selectionChanged);
3947     QFETCH(bool, cursorPositionChanged);
3948
3949     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; inputMask: \"" + inputMask + "\" }";
3950     QDeclarativeComponent textInputComponent(&engine);
3951     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3952     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3953     QVERIFY(textInput != 0);
3954
3955     textInput->select(selectionStart, selectionEnd);
3956
3957     QSignalSpy selectionSpy(textInput, SIGNAL(selectedTextChanged()));
3958     QSignalSpy selectionStartSpy(textInput, SIGNAL(selectionStartChanged()));
3959     QSignalSpy selectionEndSpy(textInput, SIGNAL(selectionEndChanged()));
3960     QSignalSpy textSpy(textInput, SIGNAL(textChanged()));
3961     QSignalSpy cursorPositionSpy(textInput, SIGNAL(cursorPositionChanged()));
3962
3963     textInput->remove(removeStart, removeEnd);
3964
3965     QCOMPARE(textInput->text(), expectedText);
3966     QCOMPARE(textInput->length(), inputMask.isEmpty() ? expectedText.length() : inputMask.length());
3967
3968     if (selectionStart > selectionEnd)  //
3969         qSwap(selectionStart, selectionEnd);
3970
3971     QCOMPARE(textInput->selectionStart(), expectedSelectionStart);
3972     QCOMPARE(textInput->selectionEnd(), expectedSelectionEnd);
3973     QCOMPARE(textInput->cursorPosition(), expectedCursorPosition);
3974
3975     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3976     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3977     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3978     QCOMPARE(textSpy.count() > 0, text != expectedText);
3979
3980     if (cursorPositionChanged)  //
3981         QVERIFY(cursorPositionSpy.count() > 0);
3982 }
3983
3984 void tst_qquicktextinput::keySequence_data()
3985 {
3986     QTest::addColumn<QString>("text");
3987     QTest::addColumn<QKeySequence>("sequence");
3988     QTest::addColumn<int>("selectionStart");
3989     QTest::addColumn<int>("selectionEnd");
3990     QTest::addColumn<int>("cursorPosition");
3991     QTest::addColumn<QString>("expectedText");
3992     QTest::addColumn<QString>("selectedText");
3993
3994     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3995
3996     QTest::newRow("select all")
3997             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3998             << 44 << standard.at(0) << standard.at(0);
3999     QTest::newRow("select end of line")
4000             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4001             << 44 << standard.at(0) << standard.at(0).mid(5);
4002     QTest::newRow("select end of document") // ### Not handled.
4003             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4004             << 3 << standard.at(0) << QString();
4005     QTest::newRow("select end of block")
4006             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4007             << 44 << standard.at(0) << standard.at(0).mid(18);
4008     QTest::newRow("delete end of line")
4009             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4010             << 24 << standard.at(0).mid(0, 24) << QString();
4011     QTest::newRow("move to start of line")
4012             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4013             << 0 << standard.at(0) << QString();
4014     QTest::newRow("move to start of block")
4015             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4016             << 0 << standard.at(0) << QString();
4017     QTest::newRow("move to next char")
4018             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4019             << 13 << standard.at(0) << QString();
4020     QTest::newRow("move to previous char")
4021             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4022             << 2 << standard.at(0) << QString();
4023     QTest::newRow("select next char")
4024             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4025             << 24 << standard.at(0) << standard.at(0).mid(23, 1);
4026     QTest::newRow("select previous char")
4027             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4028             << 18 << standard.at(0) << standard.at(0).mid(18, 1);
4029     QTest::newRow("move to next word")
4030             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4031             << 10 << standard.at(0) << QString();
4032     QTest::newRow("move to previous word")
4033             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4034             << 4 << standard.at(0) << QString();
4035     QTest::newRow("select previous word")
4036             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4037             << 10 << standard.at(0) << standard.at(0).mid(10, 1);
4038     QTest::newRow("delete (selection)")
4039             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4040             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
4041     QTest::newRow("delete (no selection)")
4042             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4043             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
4044     QTest::newRow("delete end of word")
4045             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4046             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
4047     QTest::newRow("delete start of word")
4048             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4049             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
4050 }
4051
4052 void tst_qquicktextinput::keySequence()
4053 {
4054     QFETCH(QString, text);
4055     QFETCH(QKeySequence, sequence);
4056     QFETCH(int, selectionStart);
4057     QFETCH(int, selectionEnd);
4058     QFETCH(int, cursorPosition);
4059     QFETCH(QString, expectedText);
4060     QFETCH(QString, selectedText);
4061
4062     if (sequence.isEmpty()) {
4063         QSKIP("Key sequence is undefined");
4064     }
4065
4066     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
4067     QDeclarativeComponent textInputComponent(&engine);
4068     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4069     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4070     QVERIFY(textInput != 0);
4071
4072     QQuickCanvas canvas;
4073     textInput->setParentItem(canvas.rootItem());
4074     canvas.show();
4075     canvas.requestActivateWindow();
4076     QTest::qWaitForWindowShown(&canvas);
4077     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4078
4079     textInput->select(selectionStart, selectionEnd);
4080
4081     simulateKeys(&canvas, sequence);
4082
4083     QCOMPARE(textInput->cursorPosition(), cursorPosition);
4084     QCOMPARE(textInput->text(), expectedText);
4085     QCOMPARE(textInput->selectedText(), selectedText);
4086 }
4087
4088 #define NORMAL 0
4089 #define REPLACE_UNTIL_END 1
4090
4091 void tst_qquicktextinput::undo_data()
4092 {
4093     QTest::addColumn<QStringList>("insertString");
4094     QTest::addColumn<IntList>("insertIndex");
4095     QTest::addColumn<IntList>("insertMode");
4096     QTest::addColumn<QStringList>("expectedString");
4097     QTest::addColumn<bool>("use_keys");
4098
4099     for (int i=0; i<2; i++) {
4100         QString keys_str = "keyboard";
4101         bool use_keys = true;
4102         if (i==0) {
4103             keys_str = "insert";
4104             use_keys = false;
4105         }
4106
4107         {
4108             IntList insertIndex;
4109             IntList insertMode;
4110             QStringList insertString;
4111             QStringList expectedString;
4112
4113             insertIndex << -1;
4114             insertMode << NORMAL;
4115             insertString << "1";
4116
4117             insertIndex << -1;
4118             insertMode << NORMAL;
4119             insertString << "5";
4120
4121             insertIndex << 1;
4122             insertMode << NORMAL;
4123             insertString << "3";
4124
4125             insertIndex << 1;
4126             insertMode << NORMAL;
4127             insertString << "2";
4128
4129             insertIndex << 3;
4130             insertMode << NORMAL;
4131             insertString << "4";
4132
4133             expectedString << "12345";
4134             expectedString << "1235";
4135             expectedString << "135";
4136             expectedString << "15";
4137             expectedString << "";
4138
4139             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4140                 insertString <<
4141                 insertIndex <<
4142                 insertMode <<
4143                 expectedString <<
4144                 bool(use_keys);
4145         }
4146         {
4147             IntList insertIndex;
4148             IntList insertMode;
4149             QStringList insertString;
4150             QStringList expectedString;
4151
4152             insertIndex << -1;
4153             insertMode << NORMAL;
4154             insertString << "World"; // World
4155
4156             insertIndex << 0;
4157             insertMode << NORMAL;
4158             insertString << "Hello"; // HelloWorld
4159
4160             insertIndex << 0;
4161             insertMode << NORMAL;
4162             insertString << "Well"; // WellHelloWorld
4163
4164             insertIndex << 9;
4165             insertMode << NORMAL;
4166             insertString << "There"; // WellHelloThereWorld;
4167
4168             expectedString << "WellHelloThereWorld";
4169             expectedString << "WellHelloWorld";
4170             expectedString << "HelloWorld";
4171             expectedString << "World";
4172             expectedString << "";
4173
4174             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4175                 insertString <<
4176                 insertIndex <<
4177                 insertMode <<
4178                 expectedString <<
4179                 bool(use_keys);
4180         }
4181         {
4182             IntList insertIndex;
4183             IntList insertMode;
4184             QStringList insertString;
4185             QStringList expectedString;
4186
4187             insertIndex << -1;
4188             insertMode << NORMAL;
4189             insertString << "Ensuring";
4190
4191             insertIndex << -1;
4192             insertMode << NORMAL;
4193             insertString << " instan";
4194
4195             insertIndex << 9;
4196             insertMode << NORMAL;
4197             insertString << "an ";
4198
4199             insertIndex << 10;
4200             insertMode << REPLACE_UNTIL_END;
4201             insertString << " unique instance.";
4202
4203             expectedString << "Ensuring a unique instance.";
4204             expectedString << "Ensuring an instan";
4205             expectedString << "Ensuring instan";
4206             expectedString << "";
4207
4208             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4209                 insertString <<
4210                 insertIndex <<
4211                 insertMode <<
4212                 expectedString <<
4213                 bool(use_keys);
4214         }
4215     }
4216 }
4217
4218 void tst_qquicktextinput::undo()
4219 {
4220     QFETCH(QStringList, insertString);
4221     QFETCH(IntList, insertIndex);
4222     QFETCH(IntList, insertMode);
4223     QFETCH(QStringList, expectedString);
4224
4225     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4226     QDeclarativeComponent textInputComponent(&engine);
4227     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4228     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4229     QVERIFY(textInput != 0);
4230
4231     QQuickCanvas canvas;
4232     textInput->setParentItem(canvas.rootItem());
4233     canvas.show();
4234     canvas.requestActivateWindow();
4235     QTest::qWaitForWindowShown(&canvas);
4236     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4237
4238     QVERIFY(!textInput->canUndo());
4239
4240     QSignalSpy spy(textInput, SIGNAL(canUndoChanged()));
4241
4242     int i;
4243
4244 // STEP 1: First build up an undo history by inserting or typing some strings...
4245     for (i = 0; i < insertString.size(); ++i) {
4246         if (insertIndex[i] > -1)
4247             textInput->setCursorPosition(insertIndex[i]);
4248
4249  // experimental stuff
4250         if (insertMode[i] == REPLACE_UNTIL_END) {
4251             textInput->select(insertIndex[i], insertIndex[i] + 8);
4252
4253             // This is what I actually want...
4254             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4255         }
4256
4257         for (int j = 0; j < insertString.at(i).length(); j++)
4258             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4259     }
4260
4261     QCOMPARE(spy.count(), 1);
4262
4263 // STEP 2: Next call undo several times and see if we can restore to the previous state
4264     for (i = 0; i < expectedString.size() - 1; ++i) {
4265         QCOMPARE(textInput->text(), expectedString[i]);
4266         QVERIFY(textInput->canUndo());
4267         textInput->undo();
4268     }
4269
4270 // STEP 3: Verify that we have undone everything
4271     QVERIFY(textInput->text().isEmpty());
4272     QVERIFY(!textInput->canUndo());
4273     QCOMPARE(spy.count(), 2);
4274 }
4275
4276 void tst_qquicktextinput::redo_data()
4277 {
4278     QTest::addColumn<QStringList>("insertString");
4279     QTest::addColumn<IntList>("insertIndex");
4280     QTest::addColumn<QStringList>("expectedString");
4281
4282     {
4283         IntList insertIndex;
4284         QStringList insertString;
4285         QStringList expectedString;
4286
4287         insertIndex << -1;
4288         insertString << "World"; // World
4289         insertIndex << 0;
4290         insertString << "Hello"; // HelloWorld
4291         insertIndex << 0;
4292         insertString << "Well"; // WellHelloWorld
4293         insertIndex << 9;
4294         insertString << "There"; // WellHelloThereWorld;
4295
4296         expectedString << "World";
4297         expectedString << "HelloWorld";
4298         expectedString << "WellHelloWorld";
4299         expectedString << "WellHelloThereWorld";
4300
4301         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4302     }
4303 }
4304
4305 void tst_qquicktextinput::redo()
4306 {
4307     QFETCH(QStringList, insertString);
4308     QFETCH(IntList, insertIndex);
4309     QFETCH(QStringList, expectedString);
4310
4311     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4312     QDeclarativeComponent textInputComponent(&engine);
4313     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4314     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4315     QVERIFY(textInput != 0);
4316
4317     QQuickCanvas canvas;
4318     textInput->setParentItem(canvas.rootItem());
4319     canvas.show();
4320     canvas.requestActivateWindow();
4321     QTest::qWaitForWindowShown(&canvas);
4322     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4323
4324     QVERIFY(!textInput->canUndo());
4325     QVERIFY(!textInput->canRedo());
4326
4327     QSignalSpy spy(textInput, SIGNAL(canRedoChanged()));
4328
4329     int i;
4330     // inserts the diff strings at diff positions
4331     for (i = 0; i < insertString.size(); ++i) {
4332         if (insertIndex[i] > -1)
4333             textInput->setCursorPosition(insertIndex[i]);
4334         for (int j = 0; j < insertString.at(i).length(); j++)
4335             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4336         QVERIFY(textInput->canUndo());
4337         QVERIFY(!textInput->canRedo());
4338     }
4339
4340     QCOMPARE(spy.count(), 0);
4341
4342     // undo everything
4343     while (!textInput->text().isEmpty()) {
4344         QVERIFY(textInput->canUndo());
4345         textInput->undo();
4346         QVERIFY(textInput->canRedo());
4347     }
4348
4349     QCOMPARE(spy.count(), 1);
4350
4351     for (i = 0; i < expectedString.size(); ++i) {
4352         QVERIFY(textInput->canRedo());
4353         textInput->redo();
4354         QCOMPARE(textInput->text() , expectedString[i]);
4355         QVERIFY(textInput->canUndo());
4356     }
4357     QVERIFY(!textInput->canRedo());
4358     QCOMPARE(spy.count(), 2);
4359 }
4360
4361 void tst_qquicktextinput::undo_keypressevents_data()
4362 {
4363     QTest::addColumn<KeyList>("keys");
4364     QTest::addColumn<QStringList>("expectedString");
4365
4366     {
4367         KeyList keys;
4368         QStringList expectedString;
4369
4370         keys << "AFRAID"
4371                 << QKeySequence::MoveToStartOfLine
4372                 << "VERY"
4373                 << Qt::Key_Left
4374                 << Qt::Key_Left
4375                 << Qt::Key_Left
4376                 << Qt::Key_Left
4377                 << "BE"
4378                 << QKeySequence::MoveToEndOfLine
4379                 << "!";
4380
4381         expectedString << "BEVERYAFRAID!";
4382         expectedString << "BEVERYAFRAID";
4383         expectedString << "VERYAFRAID";
4384         expectedString << "AFRAID";
4385
4386         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4387     } {
4388         KeyList keys;
4389         QStringList expectedString;
4390
4391         // inserting '1234'
4392         keys << "1234" << QKeySequence::MoveToStartOfLine
4393                 // skipping '12'
4394                 << Qt::Key_Right << Qt::Key_Right
4395                 // selecting '34'
4396                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4397                 // deleting '34'
4398                 << Qt::Key_Delete;
4399
4400         expectedString << "12";
4401         expectedString << "1234";
4402
4403         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4404     } {
4405         KeyList keys;
4406         QStringList expectedString;
4407
4408         // inserting 'AB12'
4409         keys << "AB12"
4410                 << QKeySequence::MoveToStartOfLine
4411                 // selecting 'AB'
4412                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4413                 << Qt::Key_Delete
4414                 << QKeySequence::Undo
4415                 << Qt::Key_Right
4416 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
4417                 << Qt::Key_Left
4418 #endif
4419                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4420                 << Qt::Key_Delete;
4421
4422         expectedString << "AB";
4423         expectedString << "AB12";
4424
4425         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4426     } {
4427         KeyList keys;
4428         QStringList expectedString;
4429
4430         // inserting 'ABCD'
4431         keys << "abcd"
4432                 //move left two
4433                 << Qt::Key_Left << Qt::Key_Left
4434                 // inserting '1234'
4435                 << "1234"
4436                 // selecting '1234'
4437                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4438                 // overwriting '1234' with '5'
4439                 << "5"
4440                 // undoing deletion of 'AB'
4441                 << QKeySequence::Undo
4442                 // overwriting '1234' with '6'
4443                 << "6";
4444
4445         expectedString << "ab6cd";
4446         // for versions previous to 3.2 we overwrite needed two undo operations
4447         expectedString << "ab1234cd";
4448         expectedString << "abcd";
4449
4450         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4451     } {
4452         KeyList keys;
4453         QStringList expectedString;
4454
4455         // inserting 'ABC'
4456         keys << "ABC"
4457                 // removes 'C'
4458                 << Qt::Key_Backspace;
4459
4460         expectedString << "AB";
4461         expectedString << "ABC";
4462
4463         QTest::newRow("Inserts,backspace") << keys << expectedString;
4464     } {
4465         KeyList keys;
4466         QStringList expectedString;
4467
4468         keys << "ABC"
4469                 // removes 'C'
4470                 << Qt::Key_Backspace
4471                 // inserting 'Z'
4472                 << "Z";
4473
4474         expectedString << "ABZ";
4475         expectedString << "AB";
4476         expectedString << "ABC";
4477
4478         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4479     } {
4480         KeyList keys;
4481         QStringList expectedString;
4482
4483         // inserting '123'
4484         keys << "123" << QKeySequence::MoveToStartOfLine
4485             // selecting '123'
4486              << QKeySequence::SelectEndOfLine
4487             // overwriting '123' with 'ABC'
4488              << "ABC";
4489
4490         expectedString << "ABC";
4491         // for versions previous to 3.2 we overwrite needed two undo operations
4492         expectedString << "123";
4493
4494         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4495     }
4496 }
4497
4498 void tst_qquicktextinput::undo_keypressevents()
4499 {
4500     QFETCH(KeyList, keys);
4501     QFETCH(QStringList, expectedString);
4502
4503     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
4504     QDeclarativeComponent textInputComponent(&engine);
4505     textInputComponent.setData(componentStr.toLatin1(), QUrl());
4506     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
4507     QVERIFY(textInput != 0);
4508
4509     QQuickCanvas canvas;
4510     textInput->setParentItem(canvas.rootItem());
4511     canvas.show();
4512     canvas.requestActivateWindow();
4513     QTest::qWaitForWindowShown(&canvas);
4514     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
4515
4516     simulateKeys(&canvas, keys);
4517
4518     for (int i = 0; i < expectedString.size(); ++i) {
4519         QCOMPARE(textInput->text() , expectedString[i]);
4520         textInput->undo();
4521     }
4522     QVERIFY(textInput->text().isEmpty());
4523 }
4524
4525 void tst_qquicktextinput::QTBUG_19956()
4526 {
4527     QFETCH(QString, url);
4528
4529     QQuickView canvas(testFileUrl(url));
4530     canvas.show();
4531     canvas.requestActivateWindow();
4532     QTest::qWaitForWindowShown(&canvas);
4533     QVERIFY(canvas.rootObject() != 0);
4534     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4535     QVERIFY(input);
4536     input->setFocus(true);
4537     QVERIFY(input->hasActiveFocus());
4538
4539     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
4540     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4541     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
4542     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4543
4544     canvas.rootObject()->setProperty("topvalue", 15);
4545     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
4546     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4547
4548     canvas.rootObject()->setProperty("topvalue", 25);
4549     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
4550     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4551
4552     canvas.rootObject()->setProperty("bottomvalue", 21);
4553     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
4554     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4555
4556     canvas.rootObject()->setProperty("bottomvalue", 10);
4557     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
4558     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4559 }
4560
4561 void tst_qquicktextinput::QTBUG_19956_regexp()
4562 {
4563     QUrl url = testFileUrl("qtbug-19956regexp.qml");
4564
4565     QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
4566     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
4567
4568     QQuickView canvas(url);
4569     canvas.show();
4570     canvas.requestActivateWindow();
4571     QTest::qWaitForWindowShown(&canvas);
4572     QVERIFY(canvas.rootObject() != 0);
4573     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
4574     QVERIFY(input);
4575     input->setFocus(true);
4576     QVERIFY(input->hasActiveFocus());
4577
4578     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4579     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4580     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
4581     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4582
4583     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
4584     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
4585     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
4586
4587     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
4588     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
4589     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
4590 }
4591
4592
4593 void tst_qquicktextinput::negativeDimensions()
4594 {
4595     // Verify this doesn't assert during initialization.
4596     QDeclarativeComponent component(&engine, testFileUrl("negativeDimensions.qml"));
4597     QScopedPointer<QObject> o(component.create());
4598     QVERIFY(o);
4599     QQuickTextInput *input = o->findChild<QQuickTextInput *>("input");
4600     QVERIFY(input);
4601     QCOMPARE(input->width(), qreal(-1));
4602     QCOMPARE(input->height(), qreal(-1));
4603 }
4604
4605 QTEST_MAIN(tst_qquicktextinput)
4606
4607 #include "tst_qquicktextinput.moc"