1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <qtest.h>
29#include <QtTest/QSignalSpy>
30#include "../../shared/testhttpserver.h"
31#include <math.h>
32#include <QFile>
33#include <QTextDocument>
34#include <QtQml/qqmlengine.h>
35#include <QtQml/qqmlcontext.h>
36#include <QtQml/qqmlexpression.h>
37#include <QtQml/qqmlcomponent.h>
38#include <QtGui/qguiapplication.h>
39#include <private/qquickrectangle_p.h>
40#include <private/qquicktextedit_p.h>
41#include <private/qquicktextedit_p_p.h>
42#include <private/qquicktext_p.h>
43#include <private/qquicktextdocument_p.h>
44#include <QFontMetrics>
45#include <QtQuick/QQuickView>
46#include <QDir>
47#include <QInputMethod>
48#include <QClipboard>
49#include <QMimeData>
50#include <private/qquicktextcontrol_p.h>
51#include "../../shared/util.h"
52#include "../shared/viewtestutil.h"
53#include "../../shared/platformquirks.h"
54#include "../../shared/platforminputcontext.h"
55#include <private/qinputmethod_p.h>
56#include <QtGui/qstylehints.h>
57#include <qmath.h>
58
59#ifdef Q_OS_MAC
60#include <Carbon/Carbon.h>
61#endif
62
63Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode)
64Q_DECLARE_METATYPE(Qt::Key)
65DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
66
67static bool isPlatformWayland()
68{
69 return !QGuiApplication::platformName().compare(other: QLatin1String("wayland"), cs: Qt::CaseInsensitive);
70}
71
72QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
73{
74 // XXX This will be replaced by some clever persistent platform image store.
75 QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
76 QString arch = "unknown-architecture"; // QTest needs to help with this.
77
78 QString expectfile = persistent_dir + QDir::separator() + filebasename + QLatin1Char('-') + arch + ".png";
79
80 if (!QFile::exists(fileName: expectfile)) {
81 actual.save(fileName: expectfile);
82 qWarning() << "created" << expectfile;
83 }
84
85 return expectfile;
86}
87
88typedef QPair<int, QChar> Key;
89
90class tst_qquicktextedit : public QQmlDataTest
91
92{
93 Q_OBJECT
94public:
95 tst_qquicktextedit();
96
97private slots:
98 void cleanup();
99 void text();
100 void width();
101 void wrap();
102 void textFormat();
103 void lineCount_data();
104 void lineCount();
105
106 // ### these tests may be trivial
107 void hAlign();
108 void hAlign_RightToLeft();
109 void hAlignVisual();
110 void vAlign();
111 void font();
112 void color();
113 void textMargin();
114 void persistentSelection();
115 void selectionOnFocusOut();
116 void focusOnPress();
117 void selection();
118 void overwriteMode();
119 void isRightToLeft_data();
120 void isRightToLeft();
121 void keySelection();
122 void moveCursorSelection_data();
123 void moveCursorSelection();
124 void moveCursorSelectionSequence_data();
125 void moveCursorSelectionSequence();
126 void mouseSelection_data();
127 void mouseSelection();
128 void mouseSelectionMode_data();
129 void mouseSelectionMode();
130 void dragMouseSelection();
131 void mouseSelectionMode_accessors();
132 void selectByMouse();
133 void selectByKeyboard();
134#if QT_CONFIG(shortcut)
135 void keyboardSelection_data();
136 void keyboardSelection();
137#endif
138 void renderType();
139 void inputMethodHints();
140
141 void positionAt_data();
142 void positionAt();
143
144 void linkHover();
145 void linkInteraction();
146
147 void cursorDelegate_data();
148 void cursorDelegate();
149 void remoteCursorDelegate();
150 void cursorVisible();
151 void delegateLoading_data();
152 void delegateLoading();
153 void cursorDelegateHeight();
154 void navigation();
155 void readOnly();
156#if QT_CONFIG(clipboard)
157 void copyAndPaste();
158 void canPaste();
159 void canPasteEmpty();
160 void middleClickPaste();
161#endif
162 void textInput();
163 void inputMethodUpdate();
164 void openInputPanel();
165 void geometrySignals();
166#if QT_CONFIG(clipboard)
167 void pastingRichText_QTBUG_14003();
168#endif
169 void implicitSize_data();
170 void implicitSize();
171 void implicitSize_QTBUG_63153();
172 void contentSize();
173 void boundingRect();
174 void clipRect();
175 void implicitSizeBinding_data();
176 void implicitSizeBinding();
177
178 void signal_editingfinished();
179
180 void preeditCursorRectangle();
181 void inputMethodComposing();
182 void cursorRectangleSize_data();
183 void cursorRectangleSize();
184
185 void getText_data();
186 void getText();
187 void getFormattedText_data();
188 void getFormattedText();
189 void append_data();
190 void append();
191 void insert_data();
192 void insert();
193 void remove_data();
194 void remove();
195#if QT_CONFIG(shortcut)
196 void keySequence_data();
197 void keySequence();
198#endif
199
200 void undo_data();
201 void undo();
202 void redo_data();
203 void redo();
204#if QT_CONFIG(shortcut)
205 void undo_keypressevents_data();
206 void undo_keypressevents();
207#endif
208 void clear();
209
210 void baseUrl();
211 void embeddedImages();
212 void embeddedImages_data();
213
214 void emptytags_QTBUG_22058();
215 void cursorRectangle_QTBUG_38947();
216 void textCached_QTBUG_41583();
217 void doubleSelect_QTBUG_38704();
218
219 void padding();
220 void paddingAndWrap();
221 void QTBUG_51115_readOnlyResetsSelection();
222 void keys_shortcutoverride();
223
224 void transparentSelectionColor();
225private:
226 void simulateKeys(QWindow *window, const QList<Key> &keys);
227#if QT_CONFIG(shortcut)
228 void simulateKeys(QWindow *window, const QKeySequence &sequence);
229#endif
230
231 void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = {});
232
233 QStringList standard;
234 QStringList richText;
235
236 QStringList hAlignmentStrings;
237 QStringList vAlignmentStrings;
238
239 QList<Qt::Alignment> vAlignments;
240 QList<Qt::Alignment> hAlignments;
241
242 QStringList colorStrings;
243
244 QQmlEngine engine;
245};
246
247typedef QList<int> IntList;
248Q_DECLARE_METATYPE(IntList)
249
250typedef QList<Key> KeyList;
251Q_DECLARE_METATYPE(KeyList)
252
253Q_DECLARE_METATYPE(QQuickTextEdit::HAlignment)
254Q_DECLARE_METATYPE(QQuickTextEdit::VAlignment)
255Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
256
257void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
258{
259 for (int i = 0; i < keys.count(); ++i) {
260 const int key = keys.at(i).first;
261 const int modifiers = key & Qt::KeyboardModifierMask;
262 const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
263
264 QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
265 QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
266
267 QGuiApplication::sendEvent(receiver: window, event: &press);
268 QGuiApplication::sendEvent(receiver: window, event: &release);
269 }
270}
271
272#if QT_CONFIG(shortcut)
273
274void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &sequence)
275{
276 for (int i = 0; i < sequence.count(); ++i) {
277 const int key = sequence[i];
278 const int modifiers = key & Qt::KeyboardModifierMask;
279
280 QTest::keyClick(window, key: Qt::Key(key & ~modifiers), modifier: Qt::KeyboardModifiers(modifiers));
281 }
282}
283
284QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
285{
286 for (int i = 0; i < sequence.count(); ++i)
287 keys << Key(sequence[i], QChar());
288 return keys;
289}
290
291#endif // QT_CONFIG(shortcut)
292
293template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
294{
295 for (int i = 0; i < N - 1; ++i) {
296 int key = QTest::asciiToKey(ascii: characters[i]);
297 QChar character = QLatin1Char(characters[i]);
298 keys << Key(key, character);
299 }
300 return keys;
301}
302
303QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
304{
305 keys << Key(key, QChar());
306 return keys;
307}
308
309tst_qquicktextedit::tst_qquicktextedit()
310{
311 qRegisterMetaType<QQuickTextEdit::TextFormat>();
312 qRegisterMetaType<QQuickTextEdit::SelectionMode>();
313
314 standard << "the quick brown fox jumped over the lazy dog"
315 << "the quick brown fox\n jumped over the lazy dog"
316 << "Hello, world!"
317 << "!dlrow ,olleH";
318
319 richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
320 << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
321
322 hAlignmentStrings << "AlignLeft"
323 << "AlignRight"
324 << "AlignHCenter";
325
326 vAlignmentStrings << "AlignTop"
327 << "AlignBottom"
328 << "AlignVCenter";
329
330 hAlignments << Qt::AlignLeft
331 << Qt::AlignRight
332 << Qt::AlignHCenter;
333
334 vAlignments << Qt::AlignTop
335 << Qt::AlignBottom
336 << Qt::AlignVCenter;
337
338 colorStrings << "aliceblue"
339 << "antiquewhite"
340 << "aqua"
341 << "darkkhaki"
342 << "darkolivegreen"
343 << "dimgray"
344 << "palevioletred"
345 << "lightsteelblue"
346 << "#000000"
347 << "#AAAAAA"
348 << "#FFFFFF"
349 << "#2AC05F";
350 //
351 // need a different test to do alpha channel test
352 // << "#AA0011DD"
353 // << "#00F16B11";
354 //
355}
356
357void tst_qquicktextedit::cleanup()
358{
359 // ensure not even skipped tests with custom input context leave it dangling
360 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
361 inputMethodPrivate->testContext = nullptr;
362}
363
364void tst_qquicktextedit::text()
365{
366 {
367 QQmlComponent texteditComponent(&engine);
368 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", baseUrl: QUrl());
369 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
370
371 QVERIFY(textEditObject != nullptr);
372 QCOMPARE(textEditObject->text(), QString(""));
373 QCOMPARE(textEditObject->length(), 0);
374 }
375
376 for (int i = 0; i < standard.size(); i++)
377 {
378 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
379 QQmlComponent texteditComponent(&engine);
380 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
381 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
382
383 QVERIFY(textEditObject != nullptr);
384 QCOMPARE(textEditObject->text(), standard.at(i));
385 QCOMPARE(textEditObject->length(), standard.at(i).length());
386 }
387
388 for (int i = 0; i < richText.size(); i++)
389 {
390 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
391 QQmlComponent texteditComponent(&engine);
392 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
393
394 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
395
396 QVERIFY(textEditObject != nullptr);
397
398 QString expected = richText.at(i);
399 expected.replace(rx: QRegExp("\\\\(.)"),after: "\\1");
400 QCOMPARE(textEditObject->text(), expected);
401 QCOMPARE(textEditObject->length(), expected.length());
402 }
403
404 for (int i = 0; i < standard.size(); i++)
405 {
406 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + standard.at(i) + "\" }";
407 QQmlComponent texteditComponent(&engine);
408 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
409 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
410
411 QVERIFY(textEditObject != nullptr);
412
413 QString actual = textEditObject->text();
414 QString expected = standard.at(i);
415 actual.remove(rx: QRegExp(".*<body[^>]*>"));
416 actual.remove(rx: QRegExp("(<[^>]*>)+"));
417 expected.remove(s: "\n");
418 QCOMPARE(actual.simplified(), expected);
419 QCOMPARE(textEditObject->length(), expected.length());
420 }
421
422 for (int i = 0; i < richText.size(); i++)
423 {
424 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
425 QQmlComponent texteditComponent(&engine);
426 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
427 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
428
429 QVERIFY(textEditObject != nullptr);
430 QString actual = textEditObject->text();
431 QString expected = richText.at(i);
432 actual.replace(rx: QRegExp(".*<body[^>]*>"),after: "");
433 actual.replace(rx: QRegExp("(<[^>]*>)+"),after: "<>");
434 expected.replace(rx: QRegExp("(<[^>]*>)+"),after: "<>");
435 QCOMPARE(actual.simplified(),expected.simplified());
436
437 expected.replace(before: "<>", after: " ");
438 QCOMPARE(textEditObject->length(), expected.simplified().length());
439 }
440
441 for (int i = 0; i < standard.size(); i++)
442 {
443 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + standard.at(i) + "\" }";
444 QQmlComponent texteditComponent(&engine);
445 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
446 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
447
448 QVERIFY(textEditObject != nullptr);
449 QCOMPARE(textEditObject->text(), standard.at(i));
450 QCOMPARE(textEditObject->length(), standard.at(i).length());
451 }
452
453 for (int i = 0; i < richText.size(); i++)
454 {
455 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + richText.at(i) + "\" }";
456 QQmlComponent texteditComponent(&engine);
457 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
458 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
459
460 QVERIFY(textEditObject != nullptr);
461 QString actual = textEditObject->text();
462 QString expected = richText.at(i);
463 actual.replace(rx: QRegExp(".*<body[^>]*>"),after: "");
464 actual.replace(rx: QRegExp("(<[^>]*>)+"),after: "<>");
465 expected.replace(rx: QRegExp("(<[^>]*>)+"),after: "<>");
466 QCOMPARE(actual.simplified(),expected.simplified());
467
468 expected.replace(before: "<>", after: " ");
469 QCOMPARE(textEditObject->length(), expected.simplified().length());
470 }
471}
472
473void tst_qquicktextedit::width()
474{
475 // uses Font metrics to find the width for standard and document to find the width for rich
476 {
477 QQmlComponent texteditComponent(&engine);
478 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\" }", baseUrl: QUrl());
479 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
480
481 QVERIFY(textEditObject != nullptr);
482 QCOMPARE(textEditObject->width(), 0.0);
483 }
484
485 bool requiresUnhintedMetrics = !qmlDisableDistanceField();
486
487 for (int i = 0; i < standard.size(); i++)
488 {
489 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
490 QQmlComponent texteditComponent(&engine);
491 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
492 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
493
494 QString s = standard.at(i);
495 s.replace(before: QLatin1Char('\n'), after: QChar::LineSeparator);
496
497 QTextLayout layout(s);
498 layout.setFont(textEditObject->font());
499 layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
500 if (requiresUnhintedMetrics) {
501 QTextOption option;
502 option.setUseDesignMetrics(true);
503 layout.setTextOption(option);
504 }
505
506 layout.beginLayout();
507 forever {
508 QTextLine line = layout.createLine();
509 if (!line.isValid())
510 break;
511 }
512
513 layout.endLayout();
514
515 qreal metricWidth = layout.boundingRect().width();
516
517 QVERIFY(textEditObject != nullptr);
518 QCOMPARE(textEditObject->width(), metricWidth);
519 }
520
521 for (int i = 0; i < richText.size(); i++)
522 {
523 QTextDocument document;
524 document.setHtml(richText.at(i));
525 document.setDocumentMargin(0);
526 if (requiresUnhintedMetrics)
527 document.setUseDesignMetrics(true);
528
529 qreal documentWidth = document.idealWidth();
530
531 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
532 QQmlComponent texteditComponent(&engine);
533 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
534 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
535
536 QVERIFY(textEditObject != nullptr);
537 QCOMPARE(textEditObject->width(), documentWidth);
538 }
539}
540
541void tst_qquicktextedit::wrap()
542{
543 // for specified width and wrap set true
544 {
545 QQmlComponent texteditComponent(&engine);
546 texteditComponent.setData("import QtQuick 2.0\nTextEdit { text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", baseUrl: QUrl());
547 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
548
549 QVERIFY(textEditObject != nullptr);
550 QCOMPARE(textEditObject->width(), 300.);
551 }
552
553 for (int i = 0; i < standard.size(); i++)
554 {
555 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
556 QQmlComponent texteditComponent(&engine);
557 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
558 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
559
560 QVERIFY(textEditObject != nullptr);
561 QCOMPARE(textEditObject->width(), 300.);
562 }
563
564 for (int i = 0; i < richText.size(); i++)
565 {
566 QString componentStr = "import QtQuick 2.0\nTextEdit { wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
567 QQmlComponent texteditComponent(&engine);
568 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
569 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
570
571 QVERIFY(textEditObject != nullptr);
572 QCOMPARE(textEditObject->width(), 300.);
573 }
574 {
575 QQmlComponent component(&engine);
576 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
577 QScopedPointer<QObject> object(component.create());
578 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
579 QVERIFY(edit);
580
581 QSignalSpy spy(edit, SIGNAL(wrapModeChanged()));
582
583 QCOMPARE(edit->wrapMode(), QQuickTextEdit::NoWrap);
584
585 edit->setWrapMode(QQuickTextEdit::Wrap);
586 QCOMPARE(edit->wrapMode(), QQuickTextEdit::Wrap);
587 QCOMPARE(spy.count(), 1);
588
589 edit->setWrapMode(QQuickTextEdit::Wrap);
590 QCOMPARE(spy.count(), 1);
591
592 edit->setWrapMode(QQuickTextEdit::NoWrap);
593 QCOMPARE(edit->wrapMode(), QQuickTextEdit::NoWrap);
594 QCOMPARE(spy.count(), 2);
595 }
596
597}
598
599void tst_qquicktextedit::textFormat()
600{
601 {
602 QQmlComponent textComponent(&engine);
603 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", baseUrl: QUrl::fromLocalFile(localfile: ""));
604 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(object: textComponent.create());
605
606 QVERIFY(textObject != nullptr);
607 QCOMPARE(textObject->textFormat(), QQuickTextEdit::RichText);
608 }
609 {
610 QQmlComponent textComponent(&engine);
611 textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", baseUrl: QUrl::fromLocalFile(localfile: ""));
612 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(object: textComponent.create());
613
614 QVERIFY(textObject != nullptr);
615 QCOMPARE(textObject->textFormat(), QQuickTextEdit::PlainText);
616 }
617 {
618 QQmlComponent component(&engine);
619 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
620 QScopedPointer<QObject> object(component.create());
621 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
622 QVERIFY(edit);
623
624 QSignalSpy spy(edit, &QQuickTextEdit::textFormatChanged);
625
626 QCOMPARE(edit->textFormat(), QQuickTextEdit::PlainText);
627
628 edit->setTextFormat(QQuickTextEdit::RichText);
629 QCOMPARE(edit->textFormat(), QQuickTextEdit::RichText);
630 QCOMPARE(spy.count(), 1);
631
632 edit->setTextFormat(QQuickTextEdit::RichText);
633 QCOMPARE(spy.count(), 1);
634
635 edit->setTextFormat(QQuickTextEdit::PlainText);
636 QCOMPARE(edit->textFormat(), QQuickTextEdit::PlainText);
637 QCOMPARE(spy.count(), 2);
638 }
639}
640
641static int calcLineCount(QTextDocument* doc)
642{
643 int subLines = 0;
644 for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
645 QTextLayout *layout = it.layout();
646 if (!layout)
647 continue;
648 subLines += layout->lineCount()-1;
649 }
650 return doc->lineCount() + subLines;
651}
652
653void tst_qquicktextedit::lineCount_data()
654{
655 QTest::addColumn<QStringList>(name: "texts");
656 QTest::newRow(dataTag: "plaintext") << standard;
657 QTest::newRow(dataTag: "richtext") << richText;
658}
659
660void tst_qquicktextedit::lineCount()
661{
662 QFETCH(QStringList, texts);
663
664 foreach (const QString& text, texts) {
665 QQmlComponent component(&engine);
666 component.setData("import QtQuick 2.0\nTextEdit { }", baseUrl: QUrl());
667
668 QQuickTextEdit *textedit = qobject_cast<QQuickTextEdit*>(object: component.create());
669 QVERIFY(textedit);
670
671 QTextDocument *doc = QQuickTextEditPrivate::get(item: textedit)->document;
672 QVERIFY(doc);
673
674 textedit->setText(text);
675
676 textedit->setWidth(100.0);
677 QCOMPARE(textedit->lineCount(), calcLineCount(doc));
678
679 textedit->setWrapMode(QQuickTextEdit::Wrap);
680 QCOMPARE(textedit->lineCount(), calcLineCount(doc));
681
682 textedit->setWidth(50.0);
683 QCOMPARE(textedit->lineCount(), calcLineCount(doc));
684
685 textedit->setWrapMode(QQuickTextEdit::NoWrap);
686 QCOMPARE(textedit->lineCount(), calcLineCount(doc));
687 }
688}
689
690//the alignment tests may be trivial o.oa
691void tst_qquicktextedit::hAlign()
692{
693 //test one align each, and then test if two align fails.
694
695 for (int i = 0; i < standard.size(); i++)
696 {
697 for (int j=0; j < hAlignmentStrings.size(); j++)
698 {
699 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(i: j) + "\"; text: \"" + standard.at(i) + "\" }";
700 QQmlComponent texteditComponent(&engine);
701 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
702 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
703
704 QVERIFY(textEditObject != nullptr);
705 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
706 }
707 }
708
709 for (int i = 0; i < richText.size(); i++)
710 {
711 for (int j=0; j < hAlignmentStrings.size(); j++)
712 {
713 QString componentStr = "import QtQuick 2.0\nTextEdit { horizontalAlignment: \"" + hAlignmentStrings.at(i: j) + "\"; text: \"" + richText.at(i) + "\" }";
714 QQmlComponent texteditComponent(&engine);
715 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
716 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
717
718 QVERIFY(textEditObject != nullptr);
719 QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
720 }
721 }
722
723}
724
725void tst_qquicktextedit::hAlign_RightToLeft()
726{
727 PlatformInputContext platformInputContext;
728 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
729 inputMethodPrivate->testContext = &platformInputContext;
730
731 QQuickView window(testFileUrl(fileName: "horizontalAlignment_RightToLeft.qml"));
732 QQuickTextEdit *textEdit = window.rootObject()->findChild<QQuickTextEdit*>(aName: "text");
733 QVERIFY(textEdit != nullptr);
734 window.showNormal();
735
736 const QString rtlText = textEdit->text();
737
738 // implicit alignment should follow the reading direction of text
739 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
740 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
741
742 // explicitly left aligned
743 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
744 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
745 QVERIFY(textEdit->positionToRectangle(0).x() < window.width()/2);
746
747 // explicitly right aligned
748 textEdit->setHAlign(QQuickTextEdit::AlignRight);
749 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
750 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
751
752 QString textString = textEdit->text();
753 textEdit->setText(QString("<i>") + textString + QString("</i>"));
754 textEdit->resetHAlign();
755
756 // implicitly aligned rich text should follow the reading direction of RTL text
757 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
758 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
759 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
760
761 // explicitly left aligned rich text
762 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
763 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
764 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
765 QVERIFY(textEdit->positionToRectangle(0).x() < window.width()/2);
766
767 // explicitly right aligned rich text
768 textEdit->setHAlign(QQuickTextEdit::AlignRight);
769 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
770 QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
771 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
772
773 textEdit->setText(textString);
774
775 // explicitly center aligned
776 textEdit->setHAlign(QQuickTextEdit::AlignHCenter);
777 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignHCenter);
778 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
779
780 // reseted alignment should go back to following the text reading direction
781 textEdit->resetHAlign();
782 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
783 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
784
785 // mirror the text item
786 QQuickItemPrivate::get(item: textEdit)->setLayoutMirror(true);
787
788 // mirrored implicit alignment should continue to follow the reading direction of the text
789 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
790 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
791 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
792
793 // mirrored explicitly right aligned behaves as left aligned
794 textEdit->setHAlign(QQuickTextEdit::AlignRight);
795 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
796 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
797 QVERIFY(textEdit->positionToRectangle(0).x() < window.width()/2);
798
799 // mirrored explicitly left aligned behaves as right aligned
800 textEdit->setHAlign(QQuickTextEdit::AlignLeft);
801 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
802 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
803 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
804
805 // disable mirroring
806 QQuickItemPrivate::get(item: textEdit)->setLayoutMirror(false);
807 textEdit->resetHAlign();
808
809 // English text should be implicitly left aligned
810 textEdit->setText("Hello world!");
811 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
812 QVERIFY(textEdit->positionToRectangle(0).x() < window.width()/2);
813
814 window.requestActivate();
815 QVERIFY(QTest::qWaitForWindowActive(&window));
816 QVERIFY(textEdit->hasActiveFocus());
817
818 textEdit->setText(QString());
819 { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(receiver: textEdit, event: &ev); }
820 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
821 { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(receiver: textEdit, event: &ev); }
822 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
823
824 // Clear pre-edit text. TextEdit should maybe do this itself on setText, but that may be
825 // redundant as an actual input method may take care of it.
826 { QInputMethodEvent ev; QGuiApplication::sendEvent(receiver: textEdit, event: &ev); }
827
828 // empty text with implicit alignment follows the system locale-based
829 // keyboard input direction from qApp->inputMethod()->inputDirection
830 textEdit->setText("");
831 platformInputContext.setInputDirection(Qt::LeftToRight);
832 QCOMPARE(qApp->inputMethod()->inputDirection(), Qt::LeftToRight);
833 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
834 QVERIFY(textEdit->positionToRectangle(0).x() < window.width()/2);
835
836 QSignalSpy cursorRectangleSpy(textEdit, SIGNAL(cursorRectangleChanged()));
837
838 platformInputContext.setInputDirection(Qt::RightToLeft);
839 QCOMPARE(cursorRectangleSpy.count(), 1);
840 QCOMPARE(qApp->inputMethod()->inputDirection(), Qt::RightToLeft);
841 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
842 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
843
844 // neutral text follows also input method direction
845 textEdit->resetHAlign();
846 textEdit->setText(" ()((=<>");
847 platformInputContext.setInputDirection(Qt::LeftToRight);
848 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
849 QVERIFY(textEdit->cursorRectangle().left() < window.width()/2);
850 platformInputContext.setInputDirection(Qt::RightToLeft);
851 QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
852 QVERIFY(textEdit->cursorRectangle().left() > window.width()/2);
853
854 // set input direction while having content
855 platformInputContext.setInputDirection(Qt::LeftToRight);
856 textEdit->setText("a");
857 textEdit->setCursorPosition(1);
858 platformInputContext.setInputDirection(Qt::RightToLeft);
859 QTest::keyClick(window: &window, key: Qt::Key_Backspace);
860 QVERIFY(textEdit->text().isEmpty());
861 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
862 QVERIFY(textEdit->cursorRectangle().left() > window.width()/2);
863
864 // input direction changed while not having focus
865 platformInputContext.setInputDirection(Qt::LeftToRight);
866 textEdit->setFocus(false);
867 platformInputContext.setInputDirection(Qt::RightToLeft);
868 textEdit->setFocus(true);
869 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
870 QVERIFY(textEdit->cursorRectangle().left() > window.width()/2);
871
872 textEdit->setHAlign(QQuickTextEdit::AlignRight);
873 QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
874 QVERIFY(textEdit->positionToRectangle(0).x() > window.width()/2);
875
876 // make sure editor doesn't rely on input for updating size
877 QQuickTextEdit *emptyEdit = window.rootObject()->findChild<QQuickTextEdit*>(aName: "emptyTextEdit");
878 QVERIFY(emptyEdit != nullptr);
879 platformInputContext.setInputDirection(Qt::RightToLeft);
880 emptyEdit->setFocus(true);
881 QCOMPARE(emptyEdit->hAlign(), QQuickTextEdit::AlignRight);
882 QVERIFY(emptyEdit->cursorRectangle().left() > window.width()/2);
883}
884
885
886static int numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
887{
888 int pixels = 0;
889 for (int x = fromX; x < toX; ++x) {
890 for (int y = 0; y < image.height(); ++y) {
891 if (image.pixel(x, y) != qRgb(r: 255, g: 255, b: 255))
892 pixels++;
893 }
894 }
895 return pixels;
896}
897
898static inline QByteArray msgNotGreaterThan(int n1, int n2)
899{
900 return QByteArray::number(n1) + QByteArrayLiteral(" is not greater than ") + QByteArray::number(n2);
901}
902
903static inline QByteArray msgNotLessThan(int n1, int n2)
904{
905 return QByteArray::number(n1) + QByteArrayLiteral(" is not less than ") + QByteArray::number(n2);
906}
907
908void tst_qquicktextedit::hAlignVisual()
909{
910 QQuickView view(testFileUrl(fileName: "hAlignVisual.qml"));
911 view.setFlags(view.flags() | Qt::WindowStaysOnTopHint); // Prevent being obscured by other windows.
912 view.showNormal();
913 QVERIFY(QTest::qWaitForWindowExposed(&view));
914
915 QQuickText *text = view.rootObject()->findChild<QQuickText*>(aName: "textItem");
916 QVERIFY(text != nullptr);
917
918 // Try to check whether alignment works by checking the number of black
919 // pixels in the thirds of the grabbed image.
920 const int windowWidth = 200;
921 const int textWidth = qCeil(v: text->implicitWidth());
922 QVERIFY2(textWidth < windowWidth, "System font too large.");
923 const int sectionWidth = textWidth / 3;
924 const int centeredSection1 = (windowWidth - textWidth) / 2;
925 const int centeredSection2 = centeredSection1 + sectionWidth;
926 const int centeredSection3 = centeredSection2 + sectionWidth;
927 const int centeredSection3End = centeredSection3 + sectionWidth;
928
929 {
930 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
931 || (QGuiApplication::platformName() == QLatin1String("minimal")))
932 QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimal platforms", Abort);
933
934 // Left Align
935 QImage image = view.grabWindow();
936 const int left = numberOfNonWhitePixels(fromX: centeredSection1, toX: centeredSection2, image);
937 const int mid = numberOfNonWhitePixels(fromX: centeredSection2, toX: centeredSection3, image);
938 const int right = numberOfNonWhitePixels(fromX: centeredSection3, toX: centeredSection3End, image);
939 QVERIFY2(left > mid, msgNotGreaterThan(left, mid).constData());
940 QVERIFY2(mid > right, msgNotGreaterThan(mid, right).constData());
941 }
942 {
943 // HCenter Align
944 text->setHAlign(QQuickText::AlignHCenter);
945 QImage image = view.grabWindow();
946 const int left = numberOfNonWhitePixels(fromX: centeredSection1, toX: centeredSection2, image);
947 const int mid = numberOfNonWhitePixels(fromX: centeredSection2, toX: centeredSection3, image);
948 const int right = numberOfNonWhitePixels(fromX: centeredSection3, toX: centeredSection3End, image);
949 QVERIFY2(left < mid, msgNotLessThan(left, mid).constData());
950 QVERIFY2(mid > right, msgNotGreaterThan(mid, right).constData());
951 }
952 {
953 // Right Align
954 text->setHAlign(QQuickText::AlignRight);
955 QImage image = view.grabWindow();
956 const int left = numberOfNonWhitePixels(fromX: centeredSection1, toX: centeredSection2, image);
957 const int mid = numberOfNonWhitePixels(fromX: centeredSection2, toX: centeredSection3, image);
958 const int right = numberOfNonWhitePixels(fromX: centeredSection3, toX: centeredSection3End, image);
959 QVERIFY2(left < mid, msgNotLessThan(left, mid).constData());
960 QVERIFY2(mid < right, msgNotLessThan(mid, right).constData());
961 }
962
963 text->setWidth(200);
964
965 {
966 // Left Align
967 QImage image = view.grabWindow();
968 int x = qCeil(v: text->implicitWidth());
969 int left = numberOfNonWhitePixels(fromX: 0, toX: x, image);
970 int right = numberOfNonWhitePixels(fromX: x, toX: image.width() - x, image);
971 QVERIFY2(left > 0, msgNotGreaterThan(left, 0).constData());
972 QCOMPARE(right, 0);
973 }
974 {
975 // HCenter Align
976 text->setHAlign(QQuickText::AlignHCenter);
977 QImage image = view.grabWindow();
978 int x1 = qFloor(v: image.width() - text->implicitWidth()) / 2;
979 int x2 = image.width() - x1;
980 int left = numberOfNonWhitePixels(fromX: 0, toX: x1, image);
981 int mid = numberOfNonWhitePixels(fromX: x1, toX: x2 - x1, image);
982 int right = numberOfNonWhitePixels(fromX: x2, toX: image.width() - x2, image);
983 QCOMPARE(left, 0);
984 QVERIFY2(mid > 0, msgNotGreaterThan(left, 0).constData());
985 QCOMPARE(right, 0);
986 }
987 {
988 // Right Align
989 text->setHAlign(QQuickText::AlignRight);
990 QImage image = view.grabWindow();
991 int x = image.width() - qCeil(v: text->implicitWidth());
992 int left = numberOfNonWhitePixels(fromX: 0, toX: x, image);
993 int right = numberOfNonWhitePixels(fromX: x, toX: image.width() - x, image);
994 QCOMPARE(left, 0);
995 QVERIFY2(right > 0, msgNotGreaterThan(left, 0).constData());
996 }
997}
998
999void tst_qquicktextedit::vAlign()
1000{
1001 //test one align each, and then test if two align fails.
1002
1003 for (int i = 0; i < standard.size(); i++)
1004 {
1005 for (int j=0; j < vAlignmentStrings.size(); j++)
1006 {
1007 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(i: j) + "\"; text: \"" + standard.at(i) + "\" }";
1008 QQmlComponent texteditComponent(&engine);
1009 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1010 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1011
1012 QVERIFY(textEditObject != nullptr);
1013 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
1014 }
1015 }
1016
1017 for (int i = 0; i < richText.size(); i++)
1018 {
1019 for (int j=0; j < vAlignmentStrings.size(); j++)
1020 {
1021 QString componentStr = "import QtQuick 2.0\nTextEdit { verticalAlignment: \"" + vAlignmentStrings.at(i: j) + "\"; text: \"" + richText.at(i) + "\" }";
1022 QQmlComponent texteditComponent(&engine);
1023 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1024 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1025
1026 QVERIFY(textEditObject != nullptr);
1027 QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
1028 }
1029 }
1030
1031 QQmlComponent texteditComponent(&engine);
1032 texteditComponent.setData(
1033 "import QtQuick 2.0\n"
1034 "TextEdit { width: 100; height: 100; text: \"Hello World\" }", baseUrl: QUrl());
1035 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1036
1037 QVERIFY(textEditObject != nullptr);
1038
1039 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignTop);
1040 QVERIFY(textEditObject->cursorRectangle().bottom() < 50);
1041 QVERIFY(textEditObject->positionToRectangle(0).bottom() < 50);
1042
1043 // bottom aligned
1044 textEditObject->setVAlign(QQuickTextEdit::AlignBottom);
1045 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignBottom);
1046 QVERIFY(textEditObject->cursorRectangle().top() > 50);
1047 QVERIFY(textEditObject->positionToRectangle(0).top() > 50);
1048
1049 // explicitly center aligned
1050 textEditObject->setVAlign(QQuickTextEdit::AlignVCenter);
1051 QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignVCenter);
1052 QVERIFY(textEditObject->cursorRectangle().top() < 50);
1053 QVERIFY(textEditObject->cursorRectangle().bottom() > 50);
1054 QVERIFY(textEditObject->positionToRectangle(0).top() < 50);
1055 QVERIFY(textEditObject->positionToRectangle(0).bottom() > 50);
1056
1057 // Test vertical alignment after resizing the height.
1058 textEditObject->setHeight(textEditObject->height() - 20);
1059 QVERIFY(textEditObject->cursorRectangle().top() < 40);
1060 QVERIFY(textEditObject->cursorRectangle().bottom() > 40);
1061 QVERIFY(textEditObject->positionToRectangle(0).top() < 40);
1062 QVERIFY(textEditObject->positionToRectangle(0).bottom() > 40);
1063
1064 textEditObject->setHeight(textEditObject->height() + 40);
1065 QVERIFY(textEditObject->cursorRectangle().top() < 60);
1066 QVERIFY(textEditObject->cursorRectangle().bottom() > 60);
1067 QVERIFY(textEditObject->positionToRectangle(0).top() < 60);
1068 QVERIFY(textEditObject->positionToRectangle(0).bottom() > 60);
1069}
1070
1071void tst_qquicktextedit::font()
1072{
1073 //test size, then bold, then italic, then family
1074 {
1075 QString componentStr = "import QtQuick 2.0\nTextEdit { font.pointSize: 40; text: \"Hello World\" }";
1076 QQmlComponent texteditComponent(&engine);
1077 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1078 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1079
1080 QVERIFY(textEditObject != nullptr);
1081 QCOMPARE(textEditObject->font().pointSize(), 40);
1082 QCOMPARE(textEditObject->font().bold(), false);
1083 QCOMPARE(textEditObject->font().italic(), false);
1084 }
1085
1086 {
1087 QString componentStr = "import QtQuick 2.0\nTextEdit { font.bold: true; text: \"Hello World\" }";
1088 QQmlComponent texteditComponent(&engine);
1089 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1090 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1091
1092 QVERIFY(textEditObject != nullptr);
1093 QCOMPARE(textEditObject->font().bold(), true);
1094 QCOMPARE(textEditObject->font().italic(), false);
1095 }
1096
1097 {
1098 QString componentStr = "import QtQuick 2.0\nTextEdit { font.italic: true; text: \"Hello World\" }";
1099 QQmlComponent texteditComponent(&engine);
1100 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1101 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1102
1103 QVERIFY(textEditObject != nullptr);
1104 QCOMPARE(textEditObject->font().italic(), true);
1105 QCOMPARE(textEditObject->font().bold(), false);
1106 }
1107
1108 {
1109 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"Helvetica\"; text: \"Hello World\" }";
1110 QQmlComponent texteditComponent(&engine);
1111 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1112 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1113
1114 QVERIFY(textEditObject != nullptr);
1115 QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
1116 QCOMPARE(textEditObject->font().bold(), false);
1117 QCOMPARE(textEditObject->font().italic(), false);
1118 }
1119
1120 {
1121 QString componentStr = "import QtQuick 2.0\nTextEdit { font.family: \"\"; text: \"Hello World\" }";
1122 QQmlComponent texteditComponent(&engine);
1123 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1124 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1125
1126 QVERIFY(textEditObject != nullptr);
1127 QCOMPARE(textEditObject->font().family(), QString(""));
1128 }
1129}
1130
1131void tst_qquicktextedit::color()
1132{
1133 //test initial color
1134 {
1135 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
1136 QQmlComponent texteditComponent(&engine);
1137 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1138 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1139
1140 QVERIFY(textEditObject);
1141 QCOMPARE(textEditObject->color(), QColor("black"));
1142 QCOMPARE(textEditObject->selectionColor(), QColor::fromRgba(0xFF000080));
1143 QCOMPARE(textEditObject->selectedTextColor(), QColor("white"));
1144
1145 QSignalSpy colorSpy(textEditObject, SIGNAL(colorChanged(QColor)));
1146 QSignalSpy selectionColorSpy(textEditObject, SIGNAL(selectionColorChanged(QColor)));
1147 QSignalSpy selectedTextColorSpy(textEditObject, SIGNAL(selectedTextColorChanged(QColor)));
1148
1149 textEditObject->setColor(QColor("white"));
1150 QCOMPARE(textEditObject->color(), QColor("white"));
1151 QCOMPARE(colorSpy.count(), 1);
1152
1153 textEditObject->setSelectionColor(QColor("black"));
1154 QCOMPARE(textEditObject->selectionColor(), QColor("black"));
1155 QCOMPARE(selectionColorSpy.count(), 1);
1156
1157 textEditObject->setSelectedTextColor(QColor("blue"));
1158 QCOMPARE(textEditObject->selectedTextColor(), QColor("blue"));
1159 QCOMPARE(selectedTextColorSpy.count(), 1);
1160
1161 textEditObject->setColor(QColor("white"));
1162 QCOMPARE(colorSpy.count(), 1);
1163
1164 textEditObject->setSelectionColor(QColor("black"));
1165 QCOMPARE(selectionColorSpy.count(), 1);
1166
1167 textEditObject->setSelectedTextColor(QColor("blue"));
1168 QCOMPARE(selectedTextColorSpy.count(), 1);
1169
1170 textEditObject->setColor(QColor("black"));
1171 QCOMPARE(textEditObject->color(), QColor("black"));
1172 QCOMPARE(colorSpy.count(), 2);
1173
1174 textEditObject->setSelectionColor(QColor("blue"));
1175 QCOMPARE(textEditObject->selectionColor(), QColor("blue"));
1176 QCOMPARE(selectionColorSpy.count(), 2);
1177
1178 textEditObject->setSelectedTextColor(QColor("white"));
1179 QCOMPARE(textEditObject->selectedTextColor(), QColor("white"));
1180 QCOMPARE(selectedTextColorSpy.count(), 2);
1181 }
1182
1183 //test normal
1184 for (int i = 0; i < colorStrings.size(); i++)
1185 {
1186 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1187 QQmlComponent texteditComponent(&engine);
1188 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1189 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1190 //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
1191 QVERIFY(textEditObject != nullptr);
1192 QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
1193 }
1194
1195 //test selection
1196 for (int i = 0; i < colorStrings.size(); i++)
1197 {
1198 QString componentStr = "import QtQuick 2.0\nTextEdit { selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1199 QQmlComponent texteditComponent(&engine);
1200 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1201 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1202 QVERIFY(textEditObject != nullptr);
1203 QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
1204 }
1205
1206 //test selected text
1207 for (int i = 0; i < colorStrings.size(); i++)
1208 {
1209 QString componentStr = "import QtQuick 2.0\nTextEdit { selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1210 QQmlComponent texteditComponent(&engine);
1211 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1212 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1213 QVERIFY(textEditObject != nullptr);
1214 QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
1215 }
1216
1217 {
1218 QString colorStr = "#AA001234";
1219 QColor testColor("#001234");
1220 testColor.setAlpha(170);
1221
1222 QString componentStr = "import QtQuick 2.0\nTextEdit { color: \"" + colorStr + "\"; text: \"Hello World\" }";
1223 QQmlComponent texteditComponent(&engine);
1224 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1225 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1226
1227 QVERIFY(textEditObject != nullptr);
1228 QCOMPARE(textEditObject->color(), testColor);
1229 }
1230}
1231
1232void tst_qquicktextedit::textMargin()
1233{
1234 for (qreal i=0; i<=10; i+=0.3) {
1235 QString componentStr = "import QtQuick 2.0\nTextEdit { textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
1236 QQmlComponent texteditComponent(&engine);
1237 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1238 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1239 QVERIFY(textEditObject != nullptr);
1240 QCOMPARE(textEditObject->textMargin(), i);
1241 }
1242}
1243
1244void tst_qquicktextedit::persistentSelection()
1245{
1246 QQuickView window(testFileUrl(fileName: "persistentSelection.qml"));
1247 window.show();
1248 window.requestActivate();
1249 QVERIFY(QTest::qWaitForWindowActive(&window));
1250
1251 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
1252 QVERIFY(edit);
1253 QVERIFY(edit->hasActiveFocus());
1254
1255 QSignalSpy spy(edit, SIGNAL(persistentSelectionChanged(bool)));
1256
1257 QCOMPARE(edit->persistentSelection(), false);
1258
1259 edit->setPersistentSelection(false);
1260 QCOMPARE(edit->persistentSelection(), false);
1261 QCOMPARE(spy.count(), 0);
1262
1263 edit->select(start: 1, end: 4);
1264 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1265
1266 edit->setFocus(false);
1267 QCOMPARE(edit->property("selected").toString(), QString());
1268
1269 edit->setFocus(true);
1270 QCOMPARE(edit->property("selected").toString(), QString());
1271
1272 edit->setPersistentSelection(true);
1273 QCOMPARE(edit->persistentSelection(), true);
1274 QCOMPARE(spy.count(), 1);
1275
1276 edit->select(start: 1, end: 4);
1277 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1278
1279 edit->setFocus(false);
1280 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1281
1282 edit->setFocus(true);
1283 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1284
1285 // QTBUG-50587 (persistentSelection with readOnly)
1286 edit->setReadOnly(true);
1287
1288 edit->setPersistentSelection(false);
1289 QCOMPARE(edit->persistentSelection(), false);
1290 QCOMPARE(spy.count(), 2);
1291
1292 edit->select(start: 1, end: 4);
1293 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1294
1295 edit->setFocus(false);
1296 QCOMPARE(edit->property("selected").toString(), QString());
1297
1298 edit->setFocus(true);
1299 QCOMPARE(edit->property("selected").toString(), QString());
1300
1301 edit->setPersistentSelection(true);
1302 QCOMPARE(edit->persistentSelection(), true);
1303 QCOMPARE(spy.count(), 3);
1304
1305 edit->select(start: 1, end: 4);
1306 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1307
1308 edit->setFocus(false);
1309 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1310
1311 edit->setFocus(true);
1312 QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1313}
1314
1315void tst_qquicktextedit::selectionOnFocusOut()
1316{
1317 QQuickView window(testFileUrl(fileName: "focusOutSelection.qml"));
1318 window.show();
1319 window.requestActivate();
1320 QVERIFY(QTest::qWaitForWindowActive(&window));
1321
1322 QPoint p1(25, 35);
1323 QPoint p2(25, 85);
1324
1325 QQuickTextEdit *edit1 = window.rootObject()->findChild<QQuickTextEdit*>(aName: "text1");
1326 QQuickTextEdit *edit2 = window.rootObject()->findChild<QQuickTextEdit*>(aName: "text2");
1327
1328 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
1329 QVERIFY(edit1->hasActiveFocus());
1330 QVERIFY(!edit2->hasActiveFocus());
1331
1332 edit1->selectAll();
1333 QCOMPARE(edit1->selectedText(), QLatin1String("text 1"));
1334
1335 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p2);
1336
1337 QCOMPARE(edit1->selectedText(), QLatin1String(""));
1338 QVERIFY(!edit1->hasActiveFocus());
1339 QVERIFY(edit2->hasActiveFocus());
1340
1341 edit2->selectAll();
1342 QCOMPARE(edit2->selectedText(), QLatin1String("text 2"));
1343
1344
1345 edit2->setFocus(focus: false, reason: Qt::ActiveWindowFocusReason);
1346 QVERIFY(!edit2->hasActiveFocus());
1347 QCOMPARE(edit2->selectedText(), QLatin1String("text 2"));
1348
1349 edit2->setFocus(true);
1350 QVERIFY(edit2->hasActiveFocus());
1351
1352 edit2->setFocus(focus: false, reason: Qt::PopupFocusReason);
1353 QVERIFY(edit2->hasActiveFocus());
1354 QCOMPARE(edit2->selectedText(), QLatin1String("text 2"));
1355}
1356
1357void tst_qquicktextedit::focusOnPress()
1358{
1359 QString componentStr =
1360 "import QtQuick 2.0\n"
1361 "TextEdit {\n"
1362 "property bool selectOnFocus: false\n"
1363 "width: 100; height: 50\n"
1364 "activeFocusOnPress: true\n"
1365 "text: \"Hello World\"\n"
1366 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
1367 " }";
1368 QQmlComponent texteditComponent(&engine);
1369 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1370 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1371 QVERIFY(textEditObject != nullptr);
1372 QCOMPARE(textEditObject->focusOnPress(), true);
1373 QCOMPARE(textEditObject->hasFocus(), false);
1374
1375 QSignalSpy focusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1376 QSignalSpy activeFocusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1377 QSignalSpy activeFocusOnPressSpy(textEditObject, SIGNAL(activeFocusOnPressChanged(bool)));
1378
1379 textEditObject->setFocusOnPress(true);
1380 QCOMPARE(textEditObject->focusOnPress(), true);
1381 QCOMPARE(activeFocusOnPressSpy.count(), 0);
1382
1383 QQuickWindow window;
1384 window.resize(w: 100, h: 50);
1385 textEditObject->setParentItem(window.contentItem());
1386 window.showNormal();
1387 window.requestActivate();
1388 QVERIFY(QTest::qWaitForWindowActive(&window));
1389
1390 QCOMPARE(textEditObject->hasFocus(), false);
1391 QCOMPARE(textEditObject->hasActiveFocus(), false);
1392
1393 QPoint centerPoint(window.width()/2, window.height()/2);
1394 Qt::KeyboardModifiers noModifiers;
1395 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1396 QGuiApplication::processEvents();
1397 QCOMPARE(textEditObject->hasFocus(), true);
1398 QCOMPARE(textEditObject->hasActiveFocus(), true);
1399 QCOMPARE(focusSpy.count(), 1);
1400 QCOMPARE(activeFocusSpy.count(), 1);
1401 QCOMPARE(textEditObject->selectedText(), QString());
1402 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1403
1404 textEditObject->setFocusOnPress(false);
1405 QCOMPARE(textEditObject->focusOnPress(), false);
1406 QCOMPARE(activeFocusOnPressSpy.count(), 1);
1407
1408 textEditObject->setFocus(false);
1409 QCOMPARE(textEditObject->hasFocus(), false);
1410 QCOMPARE(textEditObject->hasActiveFocus(), false);
1411 QCOMPARE(focusSpy.count(), 2);
1412 QCOMPARE(activeFocusSpy.count(), 2);
1413
1414 // Wait for double click timeout to expire before clicking again.
1415 QTest::qWait(ms: 400);
1416 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1417 QGuiApplication::processEvents();
1418 QCOMPARE(textEditObject->hasFocus(), false);
1419 QCOMPARE(textEditObject->hasActiveFocus(), false);
1420 QCOMPARE(focusSpy.count(), 2);
1421 QCOMPARE(activeFocusSpy.count(), 2);
1422 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1423
1424 textEditObject->setFocusOnPress(true);
1425 QCOMPARE(textEditObject->focusOnPress(), true);
1426 QCOMPARE(activeFocusOnPressSpy.count(), 2);
1427
1428 // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
1429 textEditObject->setProperty(name: "selectOnFocus", value: true);
1430
1431 QTest::qWait(ms: 400);
1432 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1433 QGuiApplication::processEvents();
1434 QCOMPARE(textEditObject->hasFocus(), true);
1435 QCOMPARE(textEditObject->hasActiveFocus(), true);
1436 QCOMPARE(focusSpy.count(), 3);
1437 QCOMPARE(activeFocusSpy.count(), 3);
1438 QCOMPARE(textEditObject->selectedText(), textEditObject->text());
1439 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
1440}
1441
1442void tst_qquicktextedit::selection()
1443{
1444 QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
1445 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1446 QQmlComponent texteditComponent(&engine);
1447 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1448 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
1449 QVERIFY(textEditObject != nullptr);
1450
1451
1452 //Test selection follows cursor
1453 for (int i=0; i<= testStr.size(); i++) {
1454 textEditObject->setCursorPosition(i);
1455 QCOMPARE(textEditObject->cursorPosition(), i);
1456 QCOMPARE(textEditObject->selectionStart(), i);
1457 QCOMPARE(textEditObject->selectionEnd(), i);
1458 QVERIFY(textEditObject->selectedText().isNull());
1459 }
1460
1461 textEditObject->setCursorPosition(0);
1462 QCOMPARE(textEditObject->cursorPosition(), 0);
1463 QCOMPARE(textEditObject->selectionStart(), 0);
1464 QCOMPARE(textEditObject->selectionEnd(), 0);
1465 QVERIFY(textEditObject->selectedText().isNull());
1466
1467 // Verify invalid positions are ignored.
1468 textEditObject->setCursorPosition(-1);
1469 QCOMPARE(textEditObject->cursorPosition(), 0);
1470 QCOMPARE(textEditObject->selectionStart(), 0);
1471 QCOMPARE(textEditObject->selectionEnd(), 0);
1472 QVERIFY(textEditObject->selectedText().isNull());
1473
1474 textEditObject->setCursorPosition(textEditObject->text().count()+1);
1475 QCOMPARE(textEditObject->cursorPosition(), 0);
1476 QCOMPARE(textEditObject->selectionStart(), 0);
1477 QCOMPARE(textEditObject->selectionEnd(), 0);
1478 QVERIFY(textEditObject->selectedText().isNull());
1479
1480 //Test selection
1481 for (int i=0; i<= testStr.size(); i++) {
1482 textEditObject->select(start: 0,end: i);
1483 QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
1484 }
1485 for (int i=0; i<= testStr.size(); i++) {
1486 textEditObject->select(start: i,end: testStr.size());
1487 QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
1488 }
1489
1490 textEditObject->setCursorPosition(0);
1491 QCOMPARE(textEditObject->cursorPosition(), 0);
1492 QCOMPARE(textEditObject->selectionStart(), 0);
1493 QCOMPARE(textEditObject->selectionEnd(), 0);
1494 QVERIFY(textEditObject->selectedText().isNull());
1495
1496 //Test Error Ignoring behaviour
1497 textEditObject->setCursorPosition(0);
1498 QVERIFY(textEditObject->selectedText().isNull());
1499 textEditObject->select(start: -10,end: 0);
1500 QVERIFY(textEditObject->selectedText().isNull());
1501 textEditObject->select(start: 100,end: 101);
1502 QVERIFY(textEditObject->selectedText().isNull());
1503 textEditObject->select(start: 0,end: -10);
1504 QVERIFY(textEditObject->selectedText().isNull());
1505 textEditObject->select(start: 0,end: 100);
1506 QVERIFY(textEditObject->selectedText().isNull());
1507 textEditObject->select(start: 0,end: 10);
1508 QCOMPARE(textEditObject->selectedText().size(), 10);
1509 textEditObject->select(start: -10,end: 0);
1510 QCOMPARE(textEditObject->selectedText().size(), 10);
1511 textEditObject->select(start: 100,end: 101);
1512 QCOMPARE(textEditObject->selectedText().size(), 10);
1513 textEditObject->select(start: 0,end: -10);
1514 QCOMPARE(textEditObject->selectedText().size(), 10);
1515 textEditObject->select(start: 0,end: 100);
1516 QCOMPARE(textEditObject->selectedText().size(), 10);
1517
1518 textEditObject->deselect();
1519 QVERIFY(textEditObject->selectedText().isNull());
1520 textEditObject->select(start: 0,end: 10);
1521 QCOMPARE(textEditObject->selectedText().size(), 10);
1522 textEditObject->deselect();
1523 QVERIFY(textEditObject->selectedText().isNull());
1524}
1525
1526void tst_qquicktextedit::overwriteMode()
1527{
1528 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; }";
1529 QQmlComponent textEditComponent(&engine);
1530 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1531 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
1532 QVERIFY(textEdit != nullptr);
1533
1534 QSignalSpy spy(textEdit, SIGNAL(overwriteModeChanged(bool)));
1535
1536 QQuickWindow window;
1537 textEdit->setParentItem(window.contentItem());
1538 window.show();
1539 window.requestActivate();
1540 QVERIFY(QTest::qWaitForWindowActive(&window));
1541
1542 QVERIFY(textEdit->hasActiveFocus());
1543
1544 textEdit->setOverwriteMode(true);
1545 QCOMPARE(spy.count(), 1);
1546 QCOMPARE(true, textEdit->overwriteMode());
1547 textEdit->setOverwriteMode(false);
1548 QCOMPARE(spy.count(), 2);
1549 QCOMPARE(false, textEdit->overwriteMode());
1550
1551 QVERIFY(!textEdit->overwriteMode());
1552 QString insertString = "Some first text";
1553 for (int j = 0; j < insertString.length(); j++)
1554 QTest::keyClick(window: &window, key: insertString.at(i: j).toLatin1());
1555
1556 QCOMPARE(textEdit->text(), QString("Some first text"));
1557
1558 textEdit->setOverwriteMode(true);
1559 QCOMPARE(spy.count(), 3);
1560 textEdit->setCursorPosition(5);
1561
1562 insertString = "shiny";
1563 for (int j = 0; j < insertString.length(); j++)
1564 QTest::keyClick(window: &window, key: insertString.at(i: j).toLatin1());
1565 QCOMPARE(textEdit->text(), QString("Some shiny text"));
1566
1567 textEdit->setCursorPosition(textEdit->text().length());
1568 QTest::keyClick(window: &window, key: Qt::Key_Enter);
1569
1570 textEdit->setOverwriteMode(false);
1571 QCOMPARE(spy.count(), 4);
1572
1573 insertString = "Second paragraph";
1574
1575 for (int j = 0; j < insertString.length(); j++)
1576 QTest::keyClick(window: &window, key: insertString.at(i: j).toLatin1());
1577 QCOMPARE(textEdit->lineCount(), 2);
1578
1579 textEdit->setCursorPosition(15);
1580
1581 QCOMPARE(textEdit->cursorPosition(), 15);
1582
1583 textEdit->setOverwriteMode(true);
1584 QCOMPARE(spy.count(), 5);
1585
1586 insertString = " blah";
1587 for (int j = 0; j < insertString.length(); j++)
1588 QTest::keyClick(window: &window, key: insertString.at(i: j).toLatin1());
1589 QCOMPARE(textEdit->lineCount(), 2);
1590
1591 QCOMPARE(textEdit->text(), QString("Some shiny text blah\nSecond paragraph"));
1592}
1593
1594void tst_qquicktextedit::isRightToLeft_data()
1595{
1596 QTest::addColumn<QString>(name: "text");
1597 QTest::addColumn<bool>(name: "emptyString");
1598 QTest::addColumn<bool>(name: "firstCharacter");
1599 QTest::addColumn<bool>(name: "lastCharacter");
1600 QTest::addColumn<bool>(name: "middleCharacter");
1601 QTest::addColumn<bool>(name: "startString");
1602 QTest::addColumn<bool>(name: "midString");
1603 QTest::addColumn<bool>(name: "endString");
1604
1605 const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1606 QTest::newRow(dataTag: "Empty") << "" << false << false << false << false << false << false << false;
1607 QTest::newRow(dataTag: "Neutral") << "23244242" << false << false << false << false << false << false << false;
1608 QTest::newRow(dataTag: "LTR") << "Hello world" << false << false << false << false << false << false << false;
1609 QTest::newRow(dataTag: "RTL") << QString::fromUtf16(arabic_str, size: 11) << false << true << true << true << true << true << true;
1610 QTest::newRow(dataTag: "Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, size: 11) + QString("Hello world") + QString::fromUtf16(arabic_str, size: 11) << false << true << true << false << true << true << true;
1611 QTest::newRow(dataTag: "Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, size: 11) + QString("Hello world") << false << false << false << true << false << false << false;
1612}
1613
1614void tst_qquicktextedit::isRightToLeft()
1615{
1616 QFETCH(QString, text);
1617 QFETCH(bool, emptyString);
1618 QFETCH(bool, firstCharacter);
1619 QFETCH(bool, lastCharacter);
1620 QFETCH(bool, middleCharacter);
1621 QFETCH(bool, startString);
1622 QFETCH(bool, midString);
1623 QFETCH(bool, endString);
1624
1625 QQuickTextEdit textEdit;
1626 textEdit.setText(text);
1627
1628 // first test that the right string is delivered to the QString::isRightToLeft()
1629 QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
1630 QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
1631 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
1632 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
1633 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
1634 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
1635 if (text.isEmpty())
1636 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1637 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
1638
1639 // then test that the feature actually works
1640 QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
1641 QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
1642 QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
1643 QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
1644 QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
1645 QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
1646 if (text.isEmpty())
1647 QTest::ignoreMessage(type: QtWarningMsg, message: "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1648 QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
1649}
1650
1651void tst_qquicktextedit::keySelection()
1652{
1653 QQuickView window(testFileUrl(fileName: "navigation.qml"));
1654 window.show();
1655 window.requestActivate();
1656 QVERIFY(QTest::qWaitForWindowActive(&window));
1657
1658 QVERIFY(window.rootObject() != nullptr);
1659
1660 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window.rootObject()->property(name: "myInput")));
1661
1662 QVERIFY(input != nullptr);
1663 QVERIFY(input->hasActiveFocus());
1664
1665 QSignalSpy spy(input, SIGNAL(selectedTextChanged()));
1666
1667 simulateKey(&window, key: Qt::Key_Right, modifiers: Qt::ShiftModifier);
1668 QVERIFY(input->hasActiveFocus());
1669 QCOMPARE(input->selectedText(), QString("a"));
1670 QCOMPARE(spy.count(), 1);
1671 simulateKey(&window, key: Qt::Key_Right);
1672 QVERIFY(input->hasActiveFocus());
1673 QCOMPARE(input->selectedText(), QString());
1674 QCOMPARE(spy.count(), 2);
1675 simulateKey(&window, key: Qt::Key_Right);
1676 QVERIFY(!input->hasActiveFocus());
1677 QCOMPARE(input->selectedText(), QString());
1678 QCOMPARE(spy.count(), 2);
1679
1680 simulateKey(&window, key: Qt::Key_Left);
1681 QVERIFY(input->hasActiveFocus());
1682 QCOMPARE(spy.count(), 2);
1683 simulateKey(&window, key: Qt::Key_Left, modifiers: Qt::ShiftModifier);
1684 QVERIFY(input->hasActiveFocus());
1685 QCOMPARE(input->selectedText(), QString("a"));
1686 QCOMPARE(spy.count(), 3);
1687 simulateKey(&window, key: Qt::Key_Left);
1688 QVERIFY(input->hasActiveFocus());
1689 QCOMPARE(input->selectedText(), QString());
1690 QCOMPARE(spy.count(), 4);
1691 simulateKey(&window, key: Qt::Key_Left);
1692 QVERIFY(!input->hasActiveFocus());
1693 QCOMPARE(input->selectedText(), QString());
1694 QCOMPARE(spy.count(), 4);
1695}
1696
1697void tst_qquicktextedit::moveCursorSelection_data()
1698{
1699 QTest::addColumn<QString>(name: "testStr");
1700 QTest::addColumn<int>(name: "cursorPosition");
1701 QTest::addColumn<int>(name: "movePosition");
1702 QTest::addColumn<QQuickTextEdit::SelectionMode>(name: "mode");
1703 QTest::addColumn<int>(name: "selectionStart");
1704 QTest::addColumn<int>(name: "selectionEnd");
1705 QTest::addColumn<bool>(name: "reversible");
1706
1707 QTest::newRow(dataTag: "(t)he|characters")
1708 << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true;
1709 QTest::newRow(dataTag: "do(g)|characters")
1710 << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true;
1711 QTest::newRow(dataTag: "jum(p)ed|characters")
1712 << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true;
1713 QTest::newRow(dataTag: "jumped( )over|characters")
1714 << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true;
1715 QTest::newRow(dataTag: "(the )|characters")
1716 << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true;
1717 QTest::newRow(dataTag: "( dog)|characters")
1718 << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true;
1719 QTest::newRow(dataTag: "( jumped )|characters")
1720 << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true;
1721 QTest::newRow(dataTag: "th(e qu)ick|characters")
1722 << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true;
1723 QTest::newRow(dataTag: "la(zy d)og|characters")
1724 << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true;
1725 QTest::newRow(dataTag: "jum(ped ov)er|characters")
1726 << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true;
1727 QTest::newRow(dataTag: "()the|characters")
1728 << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true;
1729 QTest::newRow(dataTag: "dog()|characters")
1730 << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true;
1731 QTest::newRow(dataTag: "jum()ped|characters")
1732 << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true;
1733
1734 QTest::newRow(dataTag: "<(t)he>|words")
1735 << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true;
1736 QTest::newRow(dataTag: "<do(g)>|words")
1737 << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true;
1738 QTest::newRow(dataTag: "<jum(p)ed>|words")
1739 << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true;
1740 QTest::newRow(dataTag: "<jumped( )>over|words")
1741 << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false;
1742 QTest::newRow(dataTag: "jumped<( )over>|words,reversed")
1743 << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false;
1744 QTest::newRow(dataTag: "<(the )>quick|words")
1745 << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false;
1746 QTest::newRow(dataTag: "<(the )quick>|words,reversed")
1747 << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false;
1748 QTest::newRow(dataTag: "<lazy( dog)>|words")
1749 << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false;
1750 QTest::newRow(dataTag: "lazy<( dog)>|words,reversed")
1751 << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false;
1752 QTest::newRow(dataTag: "<fox( jumped )>over|words")
1753 << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false;
1754 QTest::newRow(dataTag: "fox<( jumped )over>|words,reversed")
1755 << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false;
1756 QTest::newRow(dataTag: "<th(e qu)ick>|words")
1757 << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true;
1758 QTest::newRow(dataTag: "<la(zy d)og|words>")
1759 << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true;
1760 QTest::newRow(dataTag: "<jum(ped ov)er>|words")
1761 << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true;
1762 QTest::newRow(dataTag: "<()>the|words")
1763 << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1764 QTest::newRow(dataTag: "dog<()>|words")
1765 << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true;
1766 QTest::newRow(dataTag: "jum<()>ped|words")
1767 << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true;
1768
1769 QTest::newRow(dataTag: "Hello<(,)> |words")
1770 << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true;
1771 QTest::newRow(dataTag: "Hello<(, )>world|words")
1772 << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1773 QTest::newRow(dataTag: "Hello<(, )world>|words,reversed")
1774 << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1775 QTest::newRow(dataTag: "<Hel(lo, )>world|words")
1776 << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1777 QTest::newRow(dataTag: "<Hel(lo, )world>|words,reversed")
1778 << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false;
1779 QTest::newRow(dataTag: "<Hel(lo)>,|words")
1780 << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true;
1781 QTest::newRow(dataTag: "Hello<()>,|words")
1782 << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true;
1783 QTest::newRow(dataTag: "Hello,<()>|words")
1784 << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true;
1785 QTest::newRow(dataTag: "Hello<,( )>world|words")
1786 << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1787 QTest::newRow(dataTag: "Hello,<( )world>|words,reversed")
1788 << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1789 QTest::newRow(dataTag: "Hello<,( world)>|words")
1790 << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1791 QTest::newRow(dataTag: "Hello,<( world)>|words,reversed")
1792 << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1793 QTest::newRow(dataTag: "Hello<,( world!)>|words")
1794 << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false;
1795 QTest::newRow(dataTag: "Hello,<( world!)>|words,reversed")
1796 << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1797 QTest::newRow(dataTag: "Hello<(, world!)>|words")
1798 << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true;
1799 QTest::newRow(dataTag: "world<(!)>|words")
1800 << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true;
1801 QTest::newRow(dataTag: "world!<()>)|words")
1802 << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true;
1803 QTest::newRow(dataTag: "world<()>!)|words")
1804 << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true;
1805
1806 QTest::newRow(dataTag: "<(,)>olleH |words")
1807 << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true;
1808 QTest::newRow(dataTag: "<dlrow( ,)>olleH|words")
1809 << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1810 QTest::newRow(dataTag: "dlrow<( ,)>olleH|words,reversed")
1811 << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1812 QTest::newRow(dataTag: "<dlrow( ,ol)leH>|words")
1813 << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false;
1814 QTest::newRow(dataTag: "dlrow<( ,ol)leH>|words,reversed")
1815 << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1816 QTest::newRow(dataTag: ",<(ol)leH>,|words")
1817 << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true;
1818 QTest::newRow(dataTag: ",<()>olleH|words")
1819 << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true;
1820 QTest::newRow(dataTag: "<()>,olleH|words")
1821 << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true;
1822 QTest::newRow(dataTag: "<dlrow( )>,olleH|words")
1823 << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1824 QTest::newRow(dataTag: "dlrow<( ),>olleH|words,reversed")
1825 << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1826 QTest::newRow(dataTag: "<(dlrow )>,olleH|words")
1827 << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1828 QTest::newRow(dataTag: "<(dlrow ),>olleH|words,reversed")
1829 << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1830 QTest::newRow(dataTag: "<(!dlrow )>,olleH|words")
1831 << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1832 QTest::newRow(dataTag: "<(!dlrow ),>olleH|words,reversed")
1833 << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false;
1834 QTest::newRow(dataTag: "(!dlrow ,)olleH|words")
1835 << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true;
1836 QTest::newRow(dataTag: "<(!)>dlrow|words")
1837 << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true;
1838 QTest::newRow(dataTag: "<()>!dlrow|words")
1839 << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1840 QTest::newRow(dataTag: "!<()>dlrow|words")
1841 << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true;
1842}
1843
1844void tst_qquicktextedit::moveCursorSelection()
1845{
1846 QFETCH(QString, testStr);
1847 QFETCH(int, cursorPosition);
1848 QFETCH(int, movePosition);
1849 QFETCH(QQuickTextEdit::SelectionMode, mode);
1850 QFETCH(int, selectionStart);
1851 QFETCH(int, selectionEnd);
1852 QFETCH(bool, reversible);
1853
1854 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
1855 QQmlComponent textinputComponent(&engine);
1856 textinputComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
1857 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(object: textinputComponent.create());
1858 QVERIFY(texteditObject != nullptr);
1859
1860 texteditObject->setCursorPosition(cursorPosition);
1861 texteditObject->moveCursorSelection(pos: movePosition, mode);
1862
1863 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1864 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1865 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1866
1867 if (reversible) {
1868 texteditObject->setCursorPosition(movePosition);
1869 texteditObject->moveCursorSelection(pos: cursorPosition, mode);
1870
1871 QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1872 QCOMPARE(texteditObject->selectionStart(), selectionStart);
1873 QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1874 }
1875}
1876
1877void tst_qquicktextedit::moveCursorSelectionSequence_data()
1878{
1879 QTest::addColumn<QString>(name: "testStr");
1880 QTest::addColumn<int>(name: "cursorPosition");
1881 QTest::addColumn<int>(name: "movePosition1");
1882 QTest::addColumn<int>(name: "movePosition2");
1883 QTest::addColumn<int>(name: "selection1Start");
1884 QTest::addColumn<int>(name: "selection1End");
1885 QTest::addColumn<int>(name: "selection2Start");
1886 QTest::addColumn<int>(name: "selection2End");
1887
1888 QTest::newRow(dataTag: "the {<quick( bro)wn> f^ox} jumped|ltr")
1889 << standard[0]
1890 << 9 << 13 << 17
1891 << 4 << 15
1892 << 4 << 19;
1893 QTest::newRow(dataTag: "the quick<( {bro)wn> f^ox} jumped|rtl")
1894 << standard[0]
1895 << 13 << 9 << 17
1896 << 9 << 15
1897 << 10 << 19;
1898 QTest::newRow(dataTag: "the {<quick( bro)wn> ^}fox jumped|ltr")
1899 << standard[0]
1900 << 9 << 13 << 16
1901 << 4 << 15
1902 << 4 << 16;
1903 QTest::newRow(dataTag: "the quick<( {bro)wn> ^}fox jumped|rtl")
1904 << standard[0]
1905 << 13 << 9 << 16
1906 << 9 << 15
1907 << 10 << 16;
1908 QTest::newRow(dataTag: "the {<quick( bro)wn^>} fox jumped|ltr")
1909 << standard[0]
1910 << 9 << 13 << 15
1911 << 4 << 15
1912 << 4 << 15;
1913 QTest::newRow(dataTag: "the quick<( {bro)wn^>} f^ox jumped|rtl")
1914 << standard[0]
1915 << 13 << 9 << 15
1916 << 9 << 15
1917 << 10 << 15;
1918 QTest::newRow(dataTag: "the {<quick() ^}bro)wn> fox|ltr")
1919 << standard[0]
1920 << 9 << 13 << 10
1921 << 4 << 15
1922 << 4 << 10;
1923 QTest::newRow(dataTag: "the quick<(^ {^bro)wn>} fox|rtl")
1924 << standard[0]
1925 << 13 << 9 << 10
1926 << 9 << 15
1927 << 10 << 15;
1928 QTest::newRow(dataTag: "the {<quick^}( bro)wn> fox|ltr")
1929 << standard[0]
1930 << 9 << 13 << 9
1931 << 4 << 15
1932 << 4 << 9;
1933 QTest::newRow(dataTag: "the quick{<(^ bro)wn>} fox|rtl")
1934 << standard[0]
1935 << 13 << 9 << 9
1936 << 9 << 15
1937 << 9 << 15;
1938 QTest::newRow(dataTag: "the {<qui^ck}( bro)wn> fox|ltr")
1939 << standard[0]
1940 << 9 << 13 << 7
1941 << 4 << 15
1942 << 4 << 9;
1943 QTest::newRow(dataTag: "the {<qui^ck}( bro)wn> fox|rtl")
1944 << standard[0]
1945 << 13 << 9 << 7
1946 << 9 << 15
1947 << 4 << 15;
1948 QTest::newRow(dataTag: "the {<^quick}( bro)wn> fox|ltr")
1949 << standard[0]
1950 << 9 << 13 << 4
1951 << 4 << 15
1952 << 4 << 9;
1953 QTest::newRow(dataTag: "the {<^quick}( bro)wn> fox|rtl")
1954 << standard[0]
1955 << 13 << 9 << 4
1956 << 9 << 15
1957 << 4 << 15;
1958 QTest::newRow(dataTag: "the{^ <quick}( bro)wn> fox|ltr")
1959 << standard[0]
1960 << 9 << 13 << 3
1961 << 4 << 15
1962 << 3 << 9;
1963 QTest::newRow(dataTag: "the{^ <quick}( bro)wn> fox|rtl")
1964 << standard[0]
1965 << 13 << 9 << 3
1966 << 9 << 15
1967 << 3 << 15;
1968 QTest::newRow(dataTag: "{t^he <quick}( bro)wn> fox|ltr")
1969 << standard[0]
1970 << 9 << 13 << 1
1971 << 4 << 15
1972 << 0 << 9;
1973 QTest::newRow(dataTag: "{t^he <quick}( bro)wn> fox|rtl")
1974 << standard[0]
1975 << 13 << 9 << 1
1976 << 9 << 15
1977 << 0 << 15;
1978
1979 QTest::newRow(dataTag: "{<He(ll)o>, w^orld}!|ltr")
1980 << standard[2]
1981 << 2 << 4 << 8
1982 << 0 << 5
1983 << 0 << 12;
1984 QTest::newRow(dataTag: "{<He(ll)o>, w^orld}!|rtl")
1985 << standard[2]
1986 << 4 << 2 << 8
1987 << 0 << 5
1988 << 0 << 12;
1989
1990 QTest::newRow(dataTag: "!{dlro^w ,<o(ll)eH>}|ltr")
1991 << standard[3]
1992 << 9 << 11 << 5
1993 << 8 << 13
1994 << 1 << 13;
1995 QTest::newRow(dataTag: "!{dlro^w ,<o(ll)eH>}|rtl")
1996 << standard[3]
1997 << 11 << 9 << 5
1998 << 8 << 13
1999 << 1 << 13;
2000}
2001
2002void tst_qquicktextedit::moveCursorSelectionSequence()
2003{
2004 QFETCH(QString, testStr);
2005 QFETCH(int, cursorPosition);
2006 QFETCH(int, movePosition1);
2007 QFETCH(int, movePosition2);
2008 QFETCH(int, selection1Start);
2009 QFETCH(int, selection1End);
2010 QFETCH(int, selection2Start);
2011 QFETCH(int, selection2End);
2012
2013 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \""+ testStr +"\"; }";
2014 QQmlComponent texteditComponent(&engine);
2015 texteditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
2016 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(object: texteditComponent.create());
2017 QVERIFY(texteditObject != nullptr);
2018
2019 texteditObject->setCursorPosition(cursorPosition);
2020
2021 texteditObject->moveCursorSelection(pos: movePosition1, mode: QQuickTextEdit::SelectWords);
2022 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
2023 QCOMPARE(texteditObject->selectionStart(), selection1Start);
2024 QCOMPARE(texteditObject->selectionEnd(), selection1End);
2025
2026 texteditObject->moveCursorSelection(pos: movePosition2, mode: QQuickTextEdit::SelectWords);
2027 QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
2028 QCOMPARE(texteditObject->selectionStart(), selection2Start);
2029 QCOMPARE(texteditObject->selectionEnd(), selection2End);
2030}
2031
2032
2033void tst_qquicktextedit::mouseSelection_data()
2034{
2035 QTest::addColumn<QString>(name: "qmlfile");
2036 QTest::addColumn<int>(name: "from");
2037 QTest::addColumn<int>(name: "to");
2038 QTest::addColumn<QString>(name: "selectedText");
2039 QTest::addColumn<bool>(name: "focus");
2040 QTest::addColumn<bool>(name: "focusOnPress");
2041 QTest::addColumn<int>(name: "clicks");
2042
2043 // import installed
2044 QTest::newRow(dataTag: "on") << testFile(fileName: "mouseselection_true.qml") << 4 << 9 << "45678" << true << true << 1;
2045 QTest::newRow(dataTag: "off") << testFile(fileName: "mouseselection_false.qml") << 4 << 9 << QString() << true << true << 1;
2046 QTest::newRow(dataTag: "default") << testFile(fileName: "mouseselection_default.qml") << 4 << 9 << QString() << true << true << 1;
2047 QTest::newRow(dataTag: "off word selection") << testFile(fileName: "mouseselection_false_words.qml") << 4 << 9 << QString() << true << true << 1;
2048 QTest::newRow(dataTag: "on word selection (4,9)") << testFile(fileName: "mouseselection_true_words.qml") << 4 << 9 << "0123456789" << true << true << 1;
2049
2050 QTest::newRow(dataTag: "on unfocused") << testFile(fileName: "mouseselection_true.qml") << 4 << 9 << "45678" << false << false << 1;
2051 QTest::newRow(dataTag: "on word selection (4,9) unfocused") << testFile(fileName: "mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << false << 1;
2052
2053 QTest::newRow(dataTag: "on focus on press") << testFile(fileName: "mouseselection_true.qml") << 4 << 9 << "45678" << false << true << 1;
2054 QTest::newRow(dataTag: "on word selection (4,9) focus on press") << testFile(fileName: "mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << true << 1;
2055
2056 QTest::newRow(dataTag: "on word selection (2,13)") << testFile(fileName: "mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2057 QTest::newRow(dataTag: "on word selection (2,30)") << testFile(fileName: "mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2058 QTest::newRow(dataTag: "on word selection (9,13)") << testFile(fileName: "mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2059 QTest::newRow(dataTag: "on word selection (9,30)") << testFile(fileName: "mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2060 QTest::newRow(dataTag: "on word selection (13,2)") << testFile(fileName: "mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2061 QTest::newRow(dataTag: "on word selection (20,2)") << testFile(fileName: "mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2062 QTest::newRow(dataTag: "on word selection (12,9)") << testFile(fileName: "mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2063 QTest::newRow(dataTag: "on word selection (30,9)") << testFile(fileName: "mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
2064
2065 QTest::newRow(dataTag: "on double click (4,9)") << testFile(fileName: "mouseselection_true.qml") << 4 << 9 << "0123456789" << true << true << 2;
2066 QTest::newRow(dataTag: "on double click (2,13)") << testFile(fileName: "mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2067 QTest::newRow(dataTag: "on double click (2,30)") << testFile(fileName: "mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2068 QTest::newRow(dataTag: "on double click (9,13)") << testFile(fileName: "mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2069 QTest::newRow(dataTag: "on double click (9,30)") << testFile(fileName: "mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2070 QTest::newRow(dataTag: "on double click (13,2)") << testFile(fileName: "mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2071 QTest::newRow(dataTag: "on double click (20,2)") << testFile(fileName: "mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2072 QTest::newRow(dataTag: "on double click (12,9)") << testFile(fileName: "mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2073 QTest::newRow(dataTag: "on double click (30,9)") << testFile(fileName: "mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
2074
2075 QTest::newRow(dataTag: "on triple click (4,9)") << testFile(fileName: "mouseselection_true.qml") << 4 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2076 QTest::newRow(dataTag: "on triple click (2,13)") << testFile(fileName: "mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2077 QTest::newRow(dataTag: "on triple click (2,30)") << testFile(fileName: "mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2078 QTest::newRow(dataTag: "on triple click (9,13)") << testFile(fileName: "mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2079 QTest::newRow(dataTag: "on triple click (9,30)") << testFile(fileName: "mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2080 QTest::newRow(dataTag: "on triple click (13,2)") << testFile(fileName: "mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2081 QTest::newRow(dataTag: "on triple click (20,2)") << testFile(fileName: "mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2082 QTest::newRow(dataTag: "on triple click (12,9)") << testFile(fileName: "mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2083 QTest::newRow(dataTag: "on triple click (30,9)") << testFile(fileName: "mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
2084
2085 QTest::newRow(dataTag: "on triple click (2,40)") << testFile(fileName: "mouseselection_true.qml") << 2 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
2086 QTest::newRow(dataTag: "on triple click (2,50)") << testFile(fileName: "mouseselection_true.qml") << 2 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
2087 QTest::newRow(dataTag: "on triple click (25,40)") << testFile(fileName: "mouseselection_true.qml") << 25 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
2088 QTest::newRow(dataTag: "on triple click (25,50)") << testFile(fileName: "mouseselection_true.qml") << 25 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
2089 QTest::newRow(dataTag: "on triple click (40,25)") << testFile(fileName: "mouseselection_true.qml") << 40 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
2090 QTest::newRow(dataTag: "on triple click (40,50)") << testFile(fileName: "mouseselection_true.qml") << 40 << 50 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
2091 QTest::newRow(dataTag: "on triple click (50,25)") << testFile(fileName: "mouseselection_true.qml") << 50 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
2092 QTest::newRow(dataTag: "on triple click (50,40)") << testFile(fileName: "mouseselection_true.qml") << 50 << 40 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
2093
2094 QTest::newRow(dataTag: "on tr align") << testFile(fileName: "mouseselection_align_tr.qml") << 4 << 9 << "45678" << true << true << 1;
2095 QTest::newRow(dataTag: "on center align") << testFile(fileName: "mouseselection_align_center.qml") << 4 << 9 << "45678" << true << true << 1;
2096 QTest::newRow(dataTag: "on bl align") << testFile(fileName: "mouseselection_align_bl.qml") << 4 << 9 << "45678" << true << true << 1;
2097}
2098
2099void tst_qquicktextedit::mouseSelection()
2100{
2101 QFETCH(QString, qmlfile);
2102 QFETCH(int, from);
2103 QFETCH(int, to);
2104 QFETCH(QString, selectedText);
2105 QFETCH(bool, focus);
2106 QFETCH(bool, focusOnPress);
2107 QFETCH(int, clicks);
2108
2109 QQuickView window(QUrl::fromLocalFile(localfile: qmlfile));
2110
2111 window.show();
2112 window.requestActivate();
2113 QVERIFY(QTest::qWaitForWindowActive(&window));
2114
2115 QVERIFY(window.rootObject() != nullptr);
2116 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2117 QVERIFY(textEditObject != nullptr);
2118
2119 textEditObject->setFocus(focus);
2120 textEditObject->setFocusOnPress(focusOnPress);
2121
2122 // Avoid that the last click from the previous test data and the first click in the
2123 // current test data happens so close in time that they are interpreted as a double click.
2124 static const int moreThanDoubleClickInterval = QGuiApplication::styleHints()->mouseDoubleClickInterval() + 1;
2125
2126 // press-and-drag-and-release from x1 to x2
2127 QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
2128 QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
2129 if (clicks == 2)
2130 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1, delay: moreThanDoubleClickInterval);
2131 else if (clicks == 3)
2132 QTest::mouseDClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1, delay: moreThanDoubleClickInterval);
2133 // cancel the 500ms delta QTestLib adds in order to properly synthesize a triple click within the required interval
2134 QTest::lastMouseTimestamp -= QTest::mouseDoubleClickInterval;
2135 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
2136 QTest::mouseMove(window: &window, pos: p2);
2137 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p2);
2138 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
2139
2140 // Clicking and shift to clicking between the same points should select the same text.
2141 textEditObject->setCursorPosition(0);
2142 if (clicks > 1) {
2143 QTest::mouseDClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
2144 // cancel the 500ms delta QTestLib adds in order to properly synthesize a triple click within the required interval
2145 QTest::lastMouseTimestamp -= QTest::mouseDoubleClickInterval;
2146 }
2147 if (clicks != 2)
2148 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
2149 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::ShiftModifier, pos: p2);
2150 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
2151}
2152
2153void tst_qquicktextedit::dragMouseSelection()
2154{
2155 QString qmlfile = testFile(fileName: "mouseselection_true.qml");
2156
2157 QQuickView window(QUrl::fromLocalFile(localfile: qmlfile));
2158
2159 window.show();
2160 window.requestActivate();
2161 QVERIFY(QTest::qWaitForWindowActive(&window));
2162
2163 QVERIFY(window.rootObject() != nullptr);
2164 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2165 QVERIFY(textEditObject != nullptr);
2166
2167 // press-and-drag-and-release from x1 to x2
2168 int x1 = 10;
2169 int x2 = 70;
2170 int y = QFontMetrics(textEditObject->font()).height() / 2;
2171 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x1,y));
2172 QTest::mouseMove(window: &window, pos: QPoint(x2, y));
2173 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x2,y));
2174 QTest::qWait(ms: 300);
2175 QString str1;
2176 QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
2177
2178 // press and drag the current selection.
2179 x1 = 40;
2180 x2 = 100;
2181 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x1,y));
2182 QTest::mouseMove(window: &window, pos: QPoint(x2, y));
2183 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x2,y));
2184 QTest::qWait(ms: 300);
2185 QString str2;
2186 QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
2187
2188 QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
2189
2190}
2191
2192void tst_qquicktextedit::mouseSelectionMode_data()
2193{
2194 QTest::addColumn<QString>(name: "qmlfile");
2195 QTest::addColumn<bool>(name: "selectWords");
2196
2197 // import installed
2198 QTest::newRow(dataTag: "SelectWords") << testFile(fileName: "mouseselectionmode_words.qml") << true;
2199 QTest::newRow(dataTag: "SelectCharacters") << testFile(fileName: "mouseselectionmode_characters.qml") << false;
2200 QTest::newRow(dataTag: "default") << testFile(fileName: "mouseselectionmode_default.qml") << false;
2201}
2202
2203void tst_qquicktextedit::mouseSelectionMode()
2204{
2205 QFETCH(QString, qmlfile);
2206 QFETCH(bool, selectWords);
2207
2208 QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2209
2210 QQuickView window(QUrl::fromLocalFile(localfile: qmlfile));
2211
2212 window.show();
2213 window.requestActivate();
2214 QVERIFY(QTest::qWaitForWindowActive(&window));
2215
2216 QVERIFY(window.rootObject() != nullptr);
2217 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2218 QVERIFY(textEditObject != nullptr);
2219
2220 // press-and-drag-and-release from x1 to x2
2221 int x1 = 10;
2222 int x2 = 70;
2223 int y = textEditObject->height()/2;
2224 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x1,y));
2225 QTest::mouseMove(window: &window, pos: QPoint(x2, y));
2226 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: QPoint(x2,y));
2227 QString str = textEditObject->selectedText();
2228 if (selectWords) {
2229 QTRY_COMPARE(textEditObject->selectedText(), text);
2230 } else {
2231 QTRY_VERIFY(textEditObject->selectedText().length() > 3);
2232 QVERIFY(str != text);
2233 }
2234}
2235
2236void tst_qquicktextedit::mouseSelectionMode_accessors()
2237{
2238 QQmlComponent component(&engine);
2239 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
2240 QScopedPointer<QObject> object(component.create());
2241 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
2242 QVERIFY(edit);
2243
2244 QSignalSpy spy(edit, &QQuickTextEdit::mouseSelectionModeChanged);
2245
2246 QCOMPARE(edit->mouseSelectionMode(), QQuickTextEdit::SelectCharacters);
2247
2248 edit->setMouseSelectionMode(QQuickTextEdit::SelectWords);
2249 QCOMPARE(edit->mouseSelectionMode(), QQuickTextEdit::SelectWords);
2250 QCOMPARE(spy.count(), 1);
2251
2252 edit->setMouseSelectionMode(QQuickTextEdit::SelectWords);
2253 QCOMPARE(spy.count(), 1);
2254
2255 edit->setMouseSelectionMode(QQuickTextEdit::SelectCharacters);
2256 QCOMPARE(edit->mouseSelectionMode(), QQuickTextEdit::SelectCharacters);
2257 QCOMPARE(spy.count(), 2);
2258}
2259
2260void tst_qquicktextedit::selectByMouse()
2261{
2262 QQmlComponent component(&engine);
2263 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
2264 QScopedPointer<QObject> object(component.create());
2265 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
2266 QVERIFY(edit);
2267
2268 QSignalSpy spy(edit, SIGNAL(selectByMouseChanged(bool)));
2269
2270 QCOMPARE(edit->selectByMouse(), false);
2271
2272 edit->setSelectByMouse(true);
2273 QCOMPARE(edit->selectByMouse(), true);
2274 QCOMPARE(spy.count(), 1);
2275 QCOMPARE(spy.at(0).at(0).toBool(), true);
2276
2277 edit->setSelectByMouse(true);
2278 QCOMPARE(spy.count(), 1);
2279
2280 edit->setSelectByMouse(false);
2281 QCOMPARE(edit->selectByMouse(), false);
2282 QCOMPARE(spy.count(), 2);
2283 QCOMPARE(spy.at(1).at(0).toBool(), false);
2284}
2285
2286void tst_qquicktextedit::selectByKeyboard()
2287{
2288 QQmlComponent oldComponent(&engine);
2289 oldComponent.setData("import QtQuick 2.0\n TextEdit { selectByKeyboard: true }", baseUrl: QUrl());
2290 QVERIFY(!oldComponent.create());
2291
2292 QQmlComponent component(&engine);
2293 component.setData("import QtQuick 2.1\n TextEdit { }", baseUrl: QUrl());
2294 QScopedPointer<QObject> object(component.create());
2295 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
2296 QVERIFY(edit);
2297
2298 QSignalSpy spy(edit, SIGNAL(selectByKeyboardChanged(bool)));
2299
2300 QCOMPARE(edit->isReadOnly(), false);
2301 QCOMPARE(edit->selectByKeyboard(), true);
2302
2303 edit->setReadOnly(true);
2304 QCOMPARE(edit->selectByKeyboard(), false);
2305 QCOMPARE(spy.count(), 1);
2306 QCOMPARE(spy.at(0).at(0).toBool(), false);
2307
2308 edit->setSelectByKeyboard(true);
2309 QCOMPARE(edit->selectByKeyboard(), true);
2310 QCOMPARE(spy.count(), 2);
2311 QCOMPARE(spy.at(1).at(0).toBool(), true);
2312
2313 edit->setReadOnly(false);
2314 QCOMPARE(edit->selectByKeyboard(), true);
2315 QCOMPARE(spy.count(), 2);
2316
2317 edit->setSelectByKeyboard(false);
2318 QCOMPARE(edit->selectByKeyboard(), false);
2319 QCOMPARE(spy.count(), 3);
2320 QCOMPARE(spy.at(2).at(0).toBool(), false);
2321}
2322
2323#if QT_CONFIG(shortcut)
2324
2325Q_DECLARE_METATYPE(QKeySequence::StandardKey)
2326
2327void tst_qquicktextedit::keyboardSelection_data()
2328{
2329 QTest::addColumn<QString>(name: "text");
2330 QTest::addColumn<bool>(name: "readOnly");
2331 QTest::addColumn<bool>(name: "selectByKeyboard");
2332 QTest::addColumn<int>(name: "cursorPosition");
2333 QTest::addColumn<QKeySequence::StandardKey>(name: "standardKey");
2334 QTest::addColumn<QString>(name: "selectedText");
2335
2336 QTest::newRow(dataTag: "editable - select first char")
2337 << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("e");
2338 QTest::newRow(dataTag: "editable - select first word")
2339 << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("editable ");
2340
2341 QTest::newRow(dataTag: "editable - cannot select first char")
2342 << QStringLiteral("editable - cannot select first char") << false << false << 0 << QKeySequence::SelectNextChar << QStringLiteral("");
2343 QTest::newRow(dataTag: "editable - cannot select first word")
2344 << QStringLiteral("editable - cannot select first word") << false << false << 0 << QKeySequence::SelectNextWord << QStringLiteral("");
2345
2346 QTest::newRow(dataTag: "editable - select last char")
2347 << QStringLiteral("editable - select last char") << false << true << 27 << QKeySequence::SelectPreviousChar << QStringLiteral("r");
2348 QTest::newRow(dataTag: "editable - select last word")
2349 << QStringLiteral("editable - select last word") << false << true << 27 << QKeySequence::SelectPreviousWord << QStringLiteral("word");
2350
2351 QTest::newRow(dataTag: "editable - cannot select last char")
2352 << QStringLiteral("editable - cannot select last char") << false << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral("");
2353 QTest::newRow(dataTag: "editable - cannot select last word")
2354 << QStringLiteral("editable - cannot select last word") << false << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral("");
2355
2356 QTest::newRow(dataTag: "read-only - cannot select first char")
2357 << QStringLiteral("read-only - cannot select first char") << true << false << 0 << QKeySequence::SelectNextChar << QStringLiteral("");
2358 QTest::newRow(dataTag: "read-only - cannot select first word")
2359 << QStringLiteral("read-only - cannot select first word") << true << false << 0 << QKeySequence::SelectNextWord << QStringLiteral("");
2360
2361 QTest::newRow(dataTag: "read-only - cannot select last char")
2362 << QStringLiteral("read-only - cannot select last char") << true << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral("");
2363 QTest::newRow(dataTag: "read-only - cannot select last word")
2364 << QStringLiteral("read-only - cannot select last word") << true << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral("");
2365
2366 QTest::newRow(dataTag: "read-only - select first char")
2367 << QStringLiteral("read-only - select first char") << true << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("r");
2368 QTest::newRow(dataTag: "read-only - select first word")
2369 << QStringLiteral("read-only - select first word") << true << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("read");
2370
2371 QTest::newRow(dataTag: "read-only - select last char")
2372 << QStringLiteral("read-only - select last char") << true << true << 28 << QKeySequence::SelectPreviousChar << QStringLiteral("r");
2373 QTest::newRow(dataTag: "read-only - select last word")
2374 << QStringLiteral("read-only - select last word") << true << true << 28 << QKeySequence::SelectPreviousWord << QStringLiteral("word");
2375}
2376
2377void tst_qquicktextedit::keyboardSelection()
2378{
2379 QFETCH(QString, text);
2380 QFETCH(bool, readOnly);
2381 QFETCH(bool, selectByKeyboard);
2382 QFETCH(int, cursorPosition);
2383 QFETCH(QKeySequence::StandardKey, standardKey);
2384 QFETCH(QString, selectedText);
2385
2386 QQmlComponent component(&engine);
2387 component.setData("import QtQuick 2.1\n TextEdit { focus: true }", baseUrl: QUrl());
2388 QScopedPointer<QObject> object(component.create());
2389 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
2390 QVERIFY(edit);
2391
2392 edit->setText(text);
2393 edit->setSelectByKeyboard(selectByKeyboard);
2394 edit->setReadOnly(readOnly);
2395 edit->setCursorPosition(cursorPosition);
2396
2397 QQuickWindow window;
2398 edit->setParentItem(window.contentItem());
2399 window.show();
2400 window.requestActivate();
2401 QVERIFY(QTest::qWaitForWindowActive(&window));
2402 QVERIFY(edit->hasActiveFocus());
2403
2404 simulateKeys(window: &window, sequence: standardKey);
2405
2406 QCOMPARE(edit->selectedText(), selectedText);
2407}
2408
2409#endif // QT_CONFIG(shortcut)
2410
2411void tst_qquicktextedit::renderType()
2412{
2413 QQmlComponent component(&engine);
2414 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
2415 QScopedPointer<QObject> object(component.create());
2416 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
2417 QVERIFY(edit);
2418
2419 QSignalSpy spy(edit, SIGNAL(renderTypeChanged()));
2420
2421 QCOMPARE(edit->renderType(), QQuickTextEdit::QtRendering);
2422
2423 edit->setRenderType(QQuickTextEdit::NativeRendering);
2424 QCOMPARE(edit->renderType(), QQuickTextEdit::NativeRendering);
2425 QCOMPARE(spy.count(), 1);
2426
2427 edit->setRenderType(QQuickTextEdit::NativeRendering);
2428 QCOMPARE(spy.count(), 1);
2429
2430 edit->setRenderType(QQuickTextEdit::QtRendering);
2431 QCOMPARE(edit->renderType(), QQuickTextEdit::QtRendering);
2432 QCOMPARE(spy.count(), 2);
2433}
2434
2435void tst_qquicktextedit::inputMethodHints()
2436{
2437 QQuickView window(testFileUrl(fileName: "inputmethodhints.qml"));
2438 window.show();
2439 window.requestActivate();
2440
2441 QVERIFY(window.rootObject() != nullptr);
2442 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2443 QVERIFY(textEditObject != nullptr);
2444 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
2445 QSignalSpy inputMethodHintSpy(textEditObject, SIGNAL(inputMethodHintsChanged()));
2446 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
2447 QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
2448 QCOMPARE(inputMethodHintSpy.count(), 1);
2449 textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
2450 QCOMPARE(inputMethodHintSpy.count(), 1);
2451
2452 QQuickTextEdit plainTextEdit;
2453 QCOMPARE(plainTextEdit.inputMethodHints(), Qt::ImhNone);
2454}
2455
2456void tst_qquicktextedit::positionAt_data()
2457{
2458 QTest::addColumn<QQuickTextEdit::HAlignment>(name: "horizontalAlignment");
2459 QTest::addColumn<QQuickTextEdit::VAlignment>(name: "verticalAlignment");
2460
2461 QTest::newRow(dataTag: "top-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignTop;
2462 QTest::newRow(dataTag: "bottom-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignBottom;
2463 QTest::newRow(dataTag: "center-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignVCenter;
2464
2465 QTest::newRow(dataTag: "top-right") << QQuickTextEdit::AlignRight << QQuickTextEdit::AlignTop;
2466 QTest::newRow(dataTag: "top-center") << QQuickTextEdit::AlignHCenter << QQuickTextEdit::AlignTop;
2467
2468 QTest::newRow(dataTag: "center") << QQuickTextEdit::AlignHCenter << QQuickTextEdit::AlignVCenter;
2469}
2470
2471void tst_qquicktextedit::positionAt()
2472{
2473 QFETCH(QQuickTextEdit::HAlignment, horizontalAlignment);
2474 QFETCH(QQuickTextEdit::VAlignment, verticalAlignment);
2475
2476 QQuickView window(testFileUrl(fileName: "positionAt.qml"));
2477 QVERIFY(window.rootObject() != nullptr);
2478 window.show();
2479 window.requestActivate();
2480 QVERIFY(QTest::qWaitForWindowActive(&window));
2481
2482 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2483 QVERIFY(texteditObject != nullptr);
2484 texteditObject->setHAlign(horizontalAlignment);
2485 texteditObject->setVAlign(verticalAlignment);
2486
2487 QTextLayout layout(texteditObject->text().replace(before: QLatin1Char('\n'), after: QChar::LineSeparator));
2488 layout.setFont(texteditObject->font());
2489
2490 if (!qmlDisableDistanceField()) {
2491 QTextOption option;
2492 option.setUseDesignMetrics(true);
2493 layout.setTextOption(option);
2494 }
2495
2496 layout.beginLayout();
2497 QTextLine line = layout.createLine();
2498 line.setLineWidth(texteditObject->width());
2499 QTextLine secondLine = layout.createLine();
2500 secondLine.setLineWidth(texteditObject->width());
2501 layout.endLayout();
2502
2503 qreal y0 = 0;
2504 qreal y1 = 0;
2505
2506 switch (verticalAlignment) {
2507 case QQuickTextEdit::AlignTop:
2508 y0 = line.height() / 2;
2509 y1 = line.height() * 3 / 2;
2510 break;
2511 case QQuickTextEdit::AlignVCenter:
2512 y0 = (texteditObject->height() - line.height()) / 2;
2513 y1 = (texteditObject->height() + line.height()) / 2;
2514 break;
2515 case QQuickTextEdit::AlignBottom:
2516 y0 = texteditObject->height() - line.height() * 3 / 2;
2517 y1 = texteditObject->height() - line.height() / 2;
2518 break;
2519 }
2520
2521 qreal xoff = 0;
2522 switch (horizontalAlignment) {
2523 case QQuickTextEdit::AlignLeft:
2524 break;
2525 case QQuickTextEdit::AlignHCenter:
2526 xoff = (texteditObject->width() - secondLine.naturalTextWidth()) / 2;
2527 break;
2528 case QQuickTextEdit::AlignRight:
2529 xoff = texteditObject->width() - secondLine.naturalTextWidth();
2530 break;
2531 default:
2532 break;
2533 }
2534 int pos = texteditObject->positionAt(x: texteditObject->width()/2, y: y0);
2535
2536 int widthBegin = floor(x: xoff + line.cursorToX(cursorPos: pos - 1));
2537 int widthEnd = ceil(x: xoff + line.cursorToX(cursorPos: pos + 1));
2538
2539 QVERIFY(widthBegin <= texteditObject->width() / 2);
2540 QVERIFY(widthEnd >= texteditObject->width() / 2);
2541
2542 const qreal x0 = texteditObject->positionToRectangle(pos).x();
2543 const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
2544
2545 QString preeditText = texteditObject->text().mid(position: 0, n: pos);
2546 texteditObject->setText(texteditObject->text().mid(position: pos));
2547 texteditObject->setCursorPosition(0);
2548
2549 QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2550 QGuiApplication::sendEvent(receiver: texteditObject, event: &inputEvent);
2551
2552 // Check all points within the preedit text return the same position.
2553 QCOMPARE(texteditObject->positionAt(0, y0), 0);
2554 QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
2555 QCOMPARE(texteditObject->positionAt(x0, y0), 0);
2556
2557 // Verify positioning returns to normal after the preedit text.
2558 QCOMPARE(texteditObject->positionAt(x1, y0), 1);
2559 QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
2560
2561 QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
2562}
2563
2564#if QT_CONFIG(cursor)
2565void tst_qquicktextedit::linkHover()
2566{
2567 if (isPlatformWayland())
2568 QSKIP("Wayland: QCursor::setPos() doesn't work.");
2569
2570 QQuickView window(testFileUrl(fileName: "linkInteraction.qml"));
2571 window.setFlag(Qt::FramelessWindowHint);
2572 QVERIFY(window.rootObject() != nullptr);
2573 QQuickViewTestUtil::centerOnScreen(window: &window);
2574 QQuickViewTestUtil::moveMouseAway(window: &window);
2575 window.show();
2576 window.requestActivate();
2577 QVERIFY(QTest::qWaitForWindowActive(&window));
2578 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2579 QVERIFY(texteditObject != nullptr);
2580
2581 QSignalSpy hover(texteditObject, SIGNAL(linkHovered(QString)));
2582
2583 const QString link("http://example.com/");
2584 const QPoint linkPos = window.mapToGlobal(pos: texteditObject->positionToRectangle(7).center().toPoint());
2585 const QPoint textPos = window.mapToGlobal(pos: texteditObject->positionToRectangle(2).center().toPoint());
2586
2587 QCursor::setPos(linkPos);
2588 QTRY_COMPARE(hover.count(), 1);
2589 QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
2590 QCOMPARE(hover.last()[0].toString(), link);
2591
2592 QCursor::setPos(textPos);
2593 QTRY_COMPARE(hover.count(), 2);
2594 QCOMPARE(window.cursor().shape(), Qt::IBeamCursor);
2595 QCOMPARE(hover.last()[0].toString(), QString());
2596
2597 texteditObject->setCursor(Qt::OpenHandCursor);
2598
2599 QCursor::setPos(linkPos);
2600 QTRY_COMPARE(hover.count(), 3);
2601 QCOMPARE(window.cursor().shape(), Qt::PointingHandCursor);
2602 QCOMPARE(hover.last()[0].toString(), link);
2603
2604 QCursor::setPos(textPos);
2605 QTRY_COMPARE(hover.count(), 4);
2606 QCOMPARE(window.cursor().shape(), Qt::OpenHandCursor);
2607 QCOMPARE(hover.last()[0].toString(), QString());
2608}
2609#endif
2610
2611void tst_qquicktextedit::linkInteraction()
2612{
2613 QQuickView window(testFileUrl(fileName: "linkInteraction.qml"));
2614 QVERIFY(window.rootObject() != nullptr);
2615 QQuickViewTestUtil::centerOnScreen(window: &window);
2616 QQuickViewTestUtil::moveMouseAway(window: &window);
2617 window.show();
2618 window.requestActivate();
2619 QVERIFY(QTest::qWaitForWindowActive(&window));
2620
2621 QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
2622 QVERIFY(texteditObject != nullptr);
2623
2624 QSignalSpy spy(texteditObject, SIGNAL(linkActivated(QString)));
2625 QSignalSpy hover(texteditObject, SIGNAL(linkHovered(QString)));
2626
2627 const QString link("http://example.com/");
2628
2629 const QPointF linkPos = texteditObject->positionToRectangle(7).center();
2630 const QPointF textPos = texteditObject->positionToRectangle(2).center();
2631
2632 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: linkPos.toPoint());
2633 QTRY_COMPARE(spy.count(), 1);
2634 QTRY_COMPARE(hover.count(), 1);
2635 QCOMPARE(spy.last()[0].toString(), link);
2636 QCOMPARE(hover.last()[0].toString(), link);
2637 QCOMPARE(texteditObject->hoveredLink(), link);
2638 QCOMPARE(texteditObject->linkAt(linkPos.x(), linkPos.y()), link);
2639
2640 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: textPos.toPoint());
2641 QTRY_COMPARE(spy.count(), 1);
2642 QTRY_COMPARE(hover.count(), 2);
2643 QCOMPARE(hover.last()[0].toString(), QString());
2644 QCOMPARE(texteditObject->hoveredLink(), QString());
2645 QCOMPARE(texteditObject->linkAt(textPos.x(), textPos.y()), QString());
2646
2647 texteditObject->setReadOnly(true);
2648
2649 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: linkPos.toPoint());
2650 QTRY_COMPARE(spy.count(), 2);
2651 QTRY_COMPARE(hover.count(), 3);
2652 QCOMPARE(spy.last()[0].toString(), link);
2653 QCOMPARE(hover.last()[0].toString(), link);
2654 QCOMPARE(texteditObject->hoveredLink(), link);
2655 QCOMPARE(texteditObject->linkAt(linkPos.x(), linkPos.y()), link);
2656
2657 QTest::mouseClick(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: textPos.toPoint());
2658 QTRY_COMPARE(spy.count(), 2);
2659 QTRY_COMPARE(hover.count(), 4);
2660 QCOMPARE(hover.last()[0].toString(), QString());
2661 QCOMPARE(texteditObject->hoveredLink(), QString());
2662 QCOMPARE(texteditObject->linkAt(textPos.x(), textPos.y()), QString());
2663}
2664
2665void tst_qquicktextedit::cursorDelegate_data()
2666{
2667 QTest::addColumn<QUrl>(name: "source");
2668 QTest::newRow(dataTag: "out of line") << testFileUrl(fileName: "cursorTest.qml");
2669 QTest::newRow(dataTag: "in line") << testFileUrl(fileName: "cursorTestInline.qml");
2670 QTest::newRow(dataTag: "external") << testFileUrl(fileName: "cursorTestExternal.qml");
2671}
2672
2673void tst_qquicktextedit::cursorDelegate()
2674{
2675 QFETCH(QUrl, source);
2676 QQuickView view(source);
2677 view.show();
2678 view.requestActivate();
2679 QVERIFY(QTest::qWaitForWindowActive(&view));
2680 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>(aName: "textEditObject");
2681 QVERIFY(textEditObject != nullptr);
2682 // Delegate creation is deferred until focus in or cursor visibility is forced.
2683 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2684 QVERIFY(!textEditObject->isCursorVisible());
2685 //Test Delegate gets created
2686 textEditObject->setFocus(true);
2687 QVERIFY(textEditObject->isCursorVisible());
2688 QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>(aName: "cursorInstance");
2689 QVERIFY(delegateObject);
2690 QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2691 //Test Delegate gets moved
2692 for (int i=0; i<= textEditObject->text().length(); i++) {
2693 textEditObject->setCursorPosition(i);
2694 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2695 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2696 }
2697
2698 // Test delegate gets moved on mouse press.
2699 textEditObject->setSelectByMouse(true);
2700 textEditObject->setCursorPosition(0);
2701 const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
2702 QTest::qWait(ms: 400); //ensure this isn't treated as a double-click
2703 QTest::mouseClick(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: point1);
2704 QTest::qWait(ms: 50);
2705 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2706 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2707 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2708
2709 // Test delegate gets moved on mouse drag
2710 textEditObject->setCursorPosition(0);
2711 const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
2712 QTest::qWait(ms: 400); //ensure this isn't treated as a double-click
2713 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: point1);
2714 QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2715 QGuiApplication::sendEvent(receiver: &view, event: &mv);
2716 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: point2);
2717 QTest::qWait(ms: 50);
2718 QTRY_COMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2719 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2720
2721 textEditObject->setReadOnly(true);
2722 textEditObject->setCursorPosition(0);
2723 QTest::qWait(ms: 400); //ensure this isn't treated as a double-click
2724 QTest::mouseClick(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: textEditObject->positionToRectangle(5).center().toPoint());
2725 QTest::qWait(ms: 50);
2726 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2727 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2728 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2729
2730 textEditObject->setCursorPosition(0);
2731 QTest::qWait(ms: 400); //ensure this isn't treated as a double-click
2732 QTest::mouseClick(window: &view, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: textEditObject->positionToRectangle(5).center().toPoint());
2733 QTest::qWait(ms: 50);
2734 QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2735 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2736 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2737
2738 textEditObject->setCursorPosition(0);
2739 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2740 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2741
2742 textEditObject->setReadOnly(false);
2743
2744 // Delegate moved when text is entered
2745 textEditObject->setText(QString());
2746 for (int i = 0; i < 20; ++i) {
2747 QTest::keyClick(window: &view, key: Qt::Key_A);
2748 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2749 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2750 }
2751
2752 // Delegate moved when text is entered by im.
2753 textEditObject->setText(QString());
2754 for (int i = 0; i < 20; ++i) {
2755 QInputMethodEvent event;
2756 event.setCommitString(commitString: "a");
2757 QGuiApplication::sendEvent(receiver: textEditObject, event: &event);
2758 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2759 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2760 }
2761 // Delegate moved when text is removed by im.
2762 for (int i = 19; i > 1; --i) {
2763 QInputMethodEvent event;
2764 event.setCommitString(commitString: QString(), replaceFrom: -1, replaceLength: 1);
2765 QGuiApplication::sendEvent(receiver: textEditObject, event: &event);
2766 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2767 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2768 }
2769 { // Delegate moved the text is changed in place by im.
2770 QInputMethodEvent event;
2771 event.setCommitString(commitString: "i", replaceFrom: -1, replaceLength: 1);
2772 QGuiApplication::sendEvent(receiver: textEditObject, event: &event);
2773 QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2774 QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2775 }
2776
2777 //Test Delegate gets deleted
2778 textEditObject->setCursorDelegate(nullptr);
2779 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2780}
2781
2782void tst_qquicktextedit::remoteCursorDelegate()
2783{
2784 ThreadedTestHTTPServer server(dataDirectory(), TestHTTPServer::Delay);
2785
2786 QQuickView view;
2787
2788 QQmlComponent component(view.engine(), server.url(documentPath: "/RemoteCursor.qml"));
2789
2790 view.rootContext()->setContextProperty("contextDelegate", &component);
2791 view.setSource(testFileUrl(fileName: "cursorTestRemote.qml"));
2792 view.showNormal();
2793 view.requestActivate();
2794 QVERIFY(QTest::qWaitForWindowActive(&view));
2795 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>(aName: "textEditObject");
2796 QVERIFY(textEditObject != nullptr);
2797
2798 // Delegate is created on demand, and so won't be available immediately. Focus in or
2799 // setCursorVisible(true) will trigger creation.
2800 QTRY_VERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2801 QVERIFY(!textEditObject->isCursorVisible());
2802
2803 textEditObject->setFocus(true);
2804 QVERIFY(textEditObject->isCursorVisible());
2805
2806 // Wait for component to load.
2807 QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2808 QVERIFY(textEditObject->findChild<QQuickItem*>("cursorInstance"));
2809}
2810
2811void tst_qquicktextedit::cursorVisible()
2812{
2813 QQuickTextEdit edit;
2814 edit.componentComplete();
2815 QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
2816
2817 QQuickView view(testFileUrl(fileName: "cursorVisible.qml"));
2818 view.show();
2819 view.requestActivate();
2820 QVERIFY(QTest::qWaitForWindowActive(&view));
2821 QCOMPARE(&view, qGuiApp->focusWindow());
2822
2823 QCOMPARE(edit.isCursorVisible(), false);
2824
2825 edit.setCursorVisible(true);
2826 QCOMPARE(edit.isCursorVisible(), true);
2827 QCOMPARE(spy.count(), 1);
2828
2829 edit.setCursorVisible(false);
2830 QCOMPARE(edit.isCursorVisible(), false);
2831 QCOMPARE(spy.count(), 2);
2832
2833 edit.setFocus(true);
2834 QCOMPARE(edit.isCursorVisible(), false);
2835 QCOMPARE(spy.count(), 2);
2836
2837 edit.setParentItem(view.rootObject());
2838 QCOMPARE(edit.isCursorVisible(), true);
2839 QCOMPARE(spy.count(), 3);
2840
2841 edit.setFocus(false);
2842 QCOMPARE(edit.isCursorVisible(), false);
2843 QCOMPARE(spy.count(), 4);
2844
2845 edit.setFocus(true);
2846 QCOMPARE(edit.isCursorVisible(), true);
2847 QCOMPARE(spy.count(), 5);
2848
2849 QWindow alternateView;
2850 alternateView.show();
2851 alternateView.requestActivate();
2852 QVERIFY(QTest::qWaitForWindowActive(&alternateView));
2853
2854 QCOMPARE(edit.isCursorVisible(), false);
2855 QCOMPARE(spy.count(), 6);
2856
2857 view.requestActivate();
2858 QVERIFY(QTest::qWaitForWindowActive(&view));
2859 QCOMPARE(edit.isCursorVisible(), true);
2860 QCOMPARE(spy.count(), 7);
2861
2862 { // Cursor attribute with 0 length hides cursor.
2863 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2864 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2865 QCoreApplication::sendEvent(receiver: &edit, event: &ev);
2866 }
2867 QCOMPARE(edit.isCursorVisible(), false);
2868 QCOMPARE(spy.count(), 8);
2869
2870 { // Cursor attribute with non zero length shows cursor.
2871 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2872 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2873 QCoreApplication::sendEvent(receiver: &edit, event: &ev);
2874 }
2875 QCOMPARE(edit.isCursorVisible(), true);
2876 QCOMPARE(spy.count(), 9);
2877
2878
2879 { // If the cursor is hidden by the input method and the text is changed it should be visible again.
2880 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2881 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2882 QCoreApplication::sendEvent(receiver: &edit, event: &ev);
2883 }
2884 QCOMPARE(edit.isCursorVisible(), false);
2885 QCOMPARE(spy.count(), 10);
2886
2887 edit.setText("something");
2888 QCOMPARE(edit.isCursorVisible(), true);
2889 QCOMPARE(spy.count(), 11);
2890
2891 { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2892 QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2893 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2894 QCoreApplication::sendEvent(receiver: &edit, event: &ev);
2895 }
2896 QCOMPARE(edit.isCursorVisible(), false);
2897 QCOMPARE(spy.count(), 12);
2898
2899 edit.setCursorPosition(5);
2900 QCOMPARE(edit.isCursorVisible(), true);
2901 QCOMPARE(spy.count(), 13);
2902}
2903
2904void tst_qquicktextedit::delegateLoading_data()
2905{
2906 QTest::addColumn<QString>(name: "qmlfile");
2907 QTest::addColumn<QString>(name: "error");
2908
2909 // import installed
2910 QTest::newRow(dataTag: "pass") << "cursorHttpTestPass.qml" << "";
2911 QTest::newRow(dataTag: "fail1") << "cursorHttpTestFail1.qml" << "{{ServerBaseUrl}}/FailItem.qml: Remote host closed the connection";
2912 QTest::newRow(dataTag: "fail2") << "cursorHttpTestFail2.qml" << "{{ServerBaseUrl}}/ErrItem.qml:4:5: Fungus is not a type";
2913}
2914
2915void tst_qquicktextedit::delegateLoading()
2916{
2917 QFETCH(QString, qmlfile);
2918 QFETCH(QString, error);
2919
2920 QHash<QString, TestHTTPServer::Mode> dirs;
2921 dirs[testFile(fileName: "httpfail")] = TestHTTPServer::Disconnect;
2922 dirs[testFile(fileName: "httpslow")] = TestHTTPServer::Delay;
2923 dirs[testFile(fileName: "http")] = TestHTTPServer::Normal;
2924 ThreadedTestHTTPServer server(dirs);
2925
2926 error.replace(QStringLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString());
2927
2928 if (!error.isEmpty())
2929 QTest::ignoreMessage(type: QtWarningMsg, message: error.toUtf8());
2930 QQuickView view(server.url(documentPath: qmlfile));
2931 view.show();
2932 view.requestActivate();
2933
2934 if (!error.isEmpty()) {
2935 QTRY_VERIFY(view.status()==QQuickView::Error);
2936 QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
2937 } else {
2938 QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
2939 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>(aName: "textEditObject");
2940 // view.rootObject()->dumpObjectTree();
2941 QVERIFY(textEditObject != nullptr);
2942 textEditObject->setFocus(true);
2943 QQuickItem *delegate;
2944 delegate = view.rootObject()->findChild<QQuickItem*>(aName: "delegateOkay");
2945 QVERIFY(delegate);
2946 delegate = view.rootObject()->findChild<QQuickItem*>(aName: "delegateSlow");
2947 QVERIFY(delegate);
2948
2949 delete delegate;
2950 }
2951
2952
2953 //A test should be added here with a component which is ready but component.create() returns null
2954 //Not sure how to accomplish this with QQuickTextEdits cursor delegate
2955 //###This was only needed for code coverage, and could be a case of overzealous defensive programming
2956 //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB");
2957 //QVERIFY(!delegate);
2958}
2959
2960void tst_qquicktextedit::cursorDelegateHeight()
2961{
2962 QQuickView view(testFileUrl(fileName: "cursorHeight.qml"));
2963 view.show();
2964 view.requestActivate();
2965 QVERIFY(QTest::qWaitForWindowActive(&view));
2966 QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>(aName: "textEditObject");
2967 QVERIFY(textEditObject);
2968 // Delegate creation is deferred until focus in or cursor visibility is forced.
2969 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2970 QVERIFY(!textEditObject->isCursorVisible());
2971
2972 // Test that the delegate gets created.
2973 textEditObject->setFocus(true);
2974 QVERIFY(textEditObject->isCursorVisible());
2975 QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>(aName: "cursorInstance");
2976 QVERIFY(delegateObject);
2977
2978 const int largerHeight = textEditObject->cursorRectangle().height();
2979
2980 textEditObject->setCursorPosition(0);
2981 QCOMPARE(delegateObject->x(), textEditObject->cursorRectangle().x());
2982 QCOMPARE(delegateObject->y(), textEditObject->cursorRectangle().y());
2983 QCOMPARE(delegateObject->height(), textEditObject->cursorRectangle().height());
2984
2985 // Move the cursor to the next line, which has a smaller font.
2986 textEditObject->setCursorPosition(5);
2987 QCOMPARE(delegateObject->x(), textEditObject->cursorRectangle().x());
2988 QCOMPARE(delegateObject->y(), textEditObject->cursorRectangle().y());
2989 QVERIFY(textEditObject->cursorRectangle().height() < largerHeight);
2990 QCOMPARE(delegateObject->height(), textEditObject->cursorRectangle().height());
2991
2992 // Test that the delegate gets deleted
2993 textEditObject->setCursorDelegate(nullptr);
2994 QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2995}
2996
2997/*
2998TextEdit element should only handle left/right keys until the cursor reaches
2999the extent of the text, then they should ignore the keys.
3000*/
3001void tst_qquicktextedit::navigation()
3002{
3003 QQuickView window(testFileUrl(fileName: "navigation.qml"));
3004 window.show();
3005 window.requestActivate();
3006
3007 QVERIFY(window.rootObject() != nullptr);
3008
3009 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window.rootObject()->property(name: "myInput")));
3010
3011 QVERIFY(input != nullptr);
3012 QTRY_VERIFY(input->hasActiveFocus());
3013 simulateKey(&window, key: Qt::Key_Left);
3014 QVERIFY(!input->hasActiveFocus());
3015 simulateKey(&window, key: Qt::Key_Right);
3016 QVERIFY(input->hasActiveFocus());
3017 simulateKey(&window, key: Qt::Key_Right);
3018 QVERIFY(input->hasActiveFocus());
3019 simulateKey(&window, key: Qt::Key_Right);
3020 QVERIFY(!input->hasActiveFocus());
3021 simulateKey(&window, key: Qt::Key_Left);
3022 QVERIFY(input->hasActiveFocus());
3023
3024 // Test left and right navigation works if the TextEdit is empty (QTBUG-25447).
3025 input->setText(QString());
3026 QCOMPARE(input->cursorPosition(), 0);
3027 simulateKey(&window, key: Qt::Key_Right);
3028 QCOMPARE(input->hasActiveFocus(), false);
3029 simulateKey(&window, key: Qt::Key_Left);
3030 QCOMPARE(input->hasActiveFocus(), true);
3031 simulateKey(&window, key: Qt::Key_Left);
3032 QCOMPARE(input->hasActiveFocus(), false);
3033}
3034
3035#if QT_CONFIG(clipboard)
3036void tst_qquicktextedit::copyAndPaste()
3037{
3038 if (!PlatformQuirks::isClipboardAvailable())
3039 QSKIP("This machine doesn't support the clipboard");
3040
3041 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
3042 QQmlComponent textEditComponent(&engine);
3043 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
3044 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
3045 QVERIFY(textEdit != nullptr);
3046
3047 // copy and paste
3048 QCOMPARE(textEdit->text().length(), 12);
3049 textEdit->select(start: 0, end: textEdit->text().length());;
3050 textEdit->copy();
3051 QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
3052 QCOMPARE(textEdit->selectedText().length(), 12);
3053 textEdit->setCursorPosition(0);
3054 QVERIFY(textEdit->canPaste());
3055 textEdit->paste();
3056 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
3057 QCOMPARE(textEdit->text().length(), 24);
3058
3059 // canPaste
3060 QVERIFY(textEdit->canPaste());
3061 textEdit->setReadOnly(true);
3062 QVERIFY(!textEdit->canPaste());
3063 textEdit->paste();
3064 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
3065 QCOMPARE(textEdit->text().length(), 24);
3066 textEdit->setReadOnly(false);
3067 QVERIFY(textEdit->canPaste());
3068
3069 // QTBUG-12339
3070 // test that document and internal text attribute are in sync
3071 QQuickItemPrivate* pri = QQuickItemPrivate::get(item: textEdit);
3072 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri);
3073 QCOMPARE(textEdit->text(), editPrivate->text);
3074
3075 // cut: no selection
3076 textEdit->cut();
3077 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
3078
3079 // select word
3080 textEdit->setCursorPosition(0);
3081 textEdit->selectWord();
3082 QCOMPARE(textEdit->selectedText(), QString("Hello"));
3083
3084 // cut: read only.
3085 textEdit->setReadOnly(true);
3086 textEdit->cut();
3087 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
3088 textEdit->setReadOnly(false);
3089
3090 // select all and cut
3091 textEdit->selectAll();
3092 textEdit->cut();
3093 QCOMPARE(textEdit->text().length(), 0);
3094 textEdit->paste();
3095 QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
3096 QCOMPARE(textEdit->text().length(), 24);
3097
3098 // Copy first word.
3099 textEdit->setCursorPosition(0);
3100 textEdit->selectWord();
3101 textEdit->copy();
3102 // copy: no selection, previous copy retained;
3103 textEdit->setCursorPosition(0);
3104 QCOMPARE(textEdit->selectedText(), QString());
3105 textEdit->copy();
3106 textEdit->setText(QString());
3107 textEdit->paste();
3108 QCOMPARE(textEdit->text(), QString("Hello"));
3109}
3110#endif
3111
3112#if QT_CONFIG(clipboard)
3113void tst_qquicktextedit::canPaste()
3114{
3115 QGuiApplication::clipboard()->setText("Some text");
3116
3117 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
3118 QQmlComponent textEditComponent(&engine);
3119 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
3120 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
3121 QVERIFY(textEdit != nullptr);
3122
3123 // check initial value - QTBUG-17765
3124 QTextDocument document;
3125 QQuickTextControl tc(&document);
3126 QCOMPARE(textEdit->canPaste(), tc.canPaste());
3127}
3128#endif
3129
3130#if QT_CONFIG(clipboard)
3131void tst_qquicktextedit::canPasteEmpty()
3132{
3133 QGuiApplication::clipboard()->clear();
3134
3135 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
3136 QQmlComponent textEditComponent(&engine);
3137 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
3138 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
3139 QVERIFY(textEdit != nullptr);
3140
3141 // check initial value - QTBUG-17765
3142 QTextDocument document;
3143 QQuickTextControl tc(&document);
3144 QCOMPARE(textEdit->canPaste(), tc.canPaste());
3145}
3146#endif
3147
3148#if QT_CONFIG(clipboard)
3149void tst_qquicktextedit::middleClickPaste()
3150{
3151 if (!PlatformQuirks::isClipboardAvailable())
3152 QSKIP("This machine doesn't support the clipboard");
3153
3154 QQuickView window(testFileUrl(fileName: "mouseselection_true.qml"));
3155
3156 window.show();
3157 window.requestActivate();
3158 QVERIFY(QTest::qWaitForWindowActive(&window));
3159
3160 QVERIFY(window.rootObject() != nullptr);
3161 QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(object: window.rootObject());
3162 QVERIFY(textEditObject != nullptr);
3163
3164 textEditObject->setFocus(true);
3165
3166 QString originalText = textEditObject->text();
3167 QString selectedText = "234567";
3168
3169 // press-and-drag-and-release from x1 to x2
3170 const QPoint p1 = textEditObject->positionToRectangle(2).center().toPoint();
3171 const QPoint p2 = textEditObject->positionToRectangle(8).center().toPoint();
3172 const QPoint p3 = textEditObject->positionToRectangle(1).center().toPoint();
3173 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p1);
3174 QTest::mouseMove(window: &window, pos: p2);
3175 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: p2);
3176 QTRY_COMPARE(textEditObject->selectedText(), selectedText);
3177
3178 // Middle click pastes the selected text, assuming the platform supports it.
3179 QTest::mouseClick(window: &window, button: Qt::MiddleButton, stateKey: Qt::NoModifier, pos: p3);
3180
3181 if (QGuiApplication::clipboard()->supportsSelection())
3182 QCOMPARE(textEditObject->text().mid(1, selectedText.length()), selectedText);
3183 else
3184 QCOMPARE(textEditObject->text(), originalText);
3185}
3186#endif
3187
3188void tst_qquicktextedit::readOnly()
3189{
3190 QQuickView window(testFileUrl(fileName: "readOnly.qml"));
3191 window.show();
3192 window.requestActivate();
3193
3194 QVERIFY(window.rootObject() != nullptr);
3195
3196 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window.rootObject()->property(name: "myInput")));
3197
3198 QVERIFY(edit != nullptr);
3199 QTRY_VERIFY(edit->hasActiveFocus());
3200 QVERIFY(edit->isReadOnly());
3201 QString initial = edit->text();
3202 for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
3203 simulateKey(&window, key: k);
3204 simulateKey(&window, key: Qt::Key_Return);
3205 simulateKey(&window, key: Qt::Key_Space);
3206 simulateKey(&window, key: Qt::Key_Escape);
3207 QCOMPARE(edit->text(), initial);
3208
3209 edit->setCursorPosition(3);
3210 edit->setReadOnly(false);
3211 QCOMPARE(edit->isReadOnly(), false);
3212 QCOMPARE(edit->cursorPosition(), edit->text().length());
3213}
3214
3215void tst_qquicktextedit::simulateKey(QWindow *view, int key, Qt::KeyboardModifiers modifiers)
3216{
3217 QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
3218 QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
3219
3220 QGuiApplication::sendEvent(receiver: view, event: &press);
3221 QGuiApplication::sendEvent(receiver: view, event: &release);
3222}
3223
3224void tst_qquicktextedit::textInput()
3225{
3226 QQuickView view(testFileUrl(fileName: "inputMethodEvent.qml"));
3227 view.show();
3228 view.requestActivate();
3229 QVERIFY(QTest::qWaitForWindowActive(&view));
3230 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: view.rootObject());
3231 QVERIFY(edit);
3232 QVERIFY(edit->hasActiveFocus());
3233
3234 // test that input method event is committed and change signal is emitted
3235 QSignalSpy spy(edit, SIGNAL(textChanged()));
3236 QInputMethodEvent event;
3237 event.setCommitString( commitString: "Hello world!", replaceFrom: 0, replaceLength: 0);
3238 QGuiApplication::sendEvent(receiver: edit, event: &event);
3239 QCOMPARE(edit->text(), QString("Hello world!"));
3240 QCOMPARE(spy.count(), 1);
3241
3242 // QTBUG-12339
3243 // test that document and internal text attribute are in sync
3244 QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(item: edit));
3245 QCOMPARE(editPrivate->text, QString("Hello world!"));
3246
3247 QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
3248 QGuiApplication::sendEvent(receiver: edit, event: &queryEvent);
3249 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
3250
3251 edit->setReadOnly(true);
3252 QGuiApplication::sendEvent(receiver: edit, event: &queryEvent);
3253 QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
3254
3255 edit->setReadOnly(false);
3256
3257 QInputMethodEvent preeditEvent("PREEDIT", QList<QInputMethodEvent::Attribute>());
3258 QGuiApplication::sendEvent(receiver: edit, event: &preeditEvent);
3259 QCOMPARE(edit->text(), QString("Hello world!"));
3260 QCOMPARE(edit->preeditText(), QString("PREEDIT"));
3261
3262 QInputMethodEvent preeditEvent2("PREEDIT2", QList<QInputMethodEvent::Attribute>());
3263 QGuiApplication::sendEvent(receiver: edit, event: &preeditEvent2);
3264 QCOMPARE(edit->text(), QString("Hello world!"));
3265 QCOMPARE(edit->preeditText(), QString("PREEDIT2"));
3266
3267 QInputMethodEvent preeditEvent3("", QList<QInputMethodEvent::Attribute>());
3268 QGuiApplication::sendEvent(receiver: edit, event: &preeditEvent3);
3269 QCOMPARE(edit->text(), QString("Hello world!"));
3270 QCOMPARE(edit->preeditText(), QString(""));
3271}
3272
3273void tst_qquicktextedit::inputMethodUpdate()
3274{
3275 PlatformInputContext platformInputContext;
3276 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3277 inputMethodPrivate->testContext = &platformInputContext;
3278
3279 QQuickView view(testFileUrl(fileName: "inputMethodEvent.qml"));
3280 view.show();
3281 view.requestActivate();
3282 QVERIFY(QTest::qWaitForWindowActive(&view));
3283 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: view.rootObject());
3284 QVERIFY(edit);
3285 QVERIFY(edit->hasActiveFocus());
3286
3287 // text change even without cursor position change needs to trigger update
3288 edit->setText("test");
3289 platformInputContext.clear();
3290 edit->setText("xxxx");
3291 QVERIFY(platformInputContext.m_updateCallCount > 0);
3292
3293 // input method event replacing text
3294 platformInputContext.clear();
3295 {
3296 QInputMethodEvent inputMethodEvent;
3297 inputMethodEvent.setCommitString(commitString: "y", replaceFrom: -1, replaceLength: 1);
3298 QGuiApplication::sendEvent(receiver: edit, event: &inputMethodEvent);
3299 }
3300 QVERIFY(platformInputContext.m_updateCallCount > 0);
3301
3302 // input method changing selection
3303 platformInputContext.clear();
3304 {
3305 QList<QInputMethodEvent::Attribute> attributes;
3306 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
3307 QInputMethodEvent inputMethodEvent("", attributes);
3308 QGuiApplication::sendEvent(receiver: edit, event: &inputMethodEvent);
3309 }
3310 QVERIFY(edit->selectionStart() != edit->selectionEnd());
3311 QVERIFY(platformInputContext.m_updateCallCount > 0);
3312
3313 // programmatical selections trigger update
3314 platformInputContext.clear();
3315 edit->selectAll();
3316 QVERIFY(platformInputContext.m_updateCallCount > 0);
3317
3318 // font changes
3319 platformInputContext.clear();
3320 QFont font = edit->font();
3321 font.setBold(!font.bold());
3322 edit->setFont(font);
3323 QVERIFY(platformInputContext.m_updateCallCount > 0);
3324
3325 // normal input
3326 platformInputContext.clear();
3327 {
3328 QInputMethodEvent inputMethodEvent;
3329 inputMethodEvent.setCommitString(commitString: "y");
3330 QGuiApplication::sendEvent(receiver: edit, event: &inputMethodEvent);
3331 }
3332 QVERIFY(platformInputContext.m_updateCallCount > 0);
3333
3334 // changing cursor position
3335 platformInputContext.clear();
3336 edit->setCursorPosition(0);
3337 QVERIFY(platformInputContext.m_updateCallCount > 0);
3338
3339 // continuing with selection
3340 platformInputContext.clear();
3341 edit->moveCursorSelection(pos: 1);
3342 QVERIFY(platformInputContext.m_updateCallCount > 0);
3343
3344 // read only disabled input method
3345 platformInputContext.clear();
3346 edit->setReadOnly(true);
3347 QVERIFY(platformInputContext.m_updateCallCount > 0);
3348 edit->setReadOnly(false);
3349
3350 // no updates while no focus
3351 edit->setFocus(false);
3352 platformInputContext.clear();
3353 edit->setText("Foo");
3354 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3355 edit->setCursorPosition(1);
3356 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3357 edit->selectAll();
3358 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3359 edit->setReadOnly(true);
3360 QCOMPARE(platformInputContext.m_updateCallCount, 0);
3361}
3362
3363void tst_qquicktextedit::openInputPanel()
3364{
3365 PlatformInputContext platformInputContext;
3366 QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
3367 inputMethodPrivate->testContext = &platformInputContext;
3368
3369 QQuickView view(testFileUrl(fileName: "openInputPanel.qml"));
3370 view.showNormal();
3371 view.requestActivate();
3372 QVERIFY(QTest::qWaitForWindowActive(&view));
3373
3374 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: view.rootObject());
3375 QVERIFY(edit);
3376
3377 // check default values
3378 QVERIFY(edit->focusOnPress());
3379 QVERIFY(!edit->hasActiveFocus());
3380 QVERIFY(qApp->focusObject() != edit);
3381
3382 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3383
3384 // input panel should open on focus
3385 QPoint centerPoint(view.width()/2, view.height()/2);
3386 Qt::KeyboardModifiers noModifiers = Qt::NoModifier;
3387 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3388 QGuiApplication::processEvents();
3389 QVERIFY(edit->hasActiveFocus());
3390 QCOMPARE(qApp->focusObject(), edit);
3391 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3392 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3393
3394 // input panel should be re-opened when pressing already focused TextEdit
3395 qApp->inputMethod()->hide();
3396 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3397 QVERIFY(edit->hasActiveFocus());
3398 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3399 QGuiApplication::processEvents();
3400 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3401 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3402
3403 // input panel should stay visible if focus is lost to another text editor
3404 QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
3405 QQuickTextEdit anotherEdit;
3406 anotherEdit.setParentItem(view.rootObject());
3407 anotherEdit.setFocus(true);
3408 QCOMPARE(qApp->inputMethod()->isVisible(), true);
3409 QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherEdit));
3410 QCOMPARE(inputPanelVisibilitySpy.count(), 0);
3411
3412 anotherEdit.setFocus(false);
3413 QVERIFY(qApp->focusObject() != &anotherEdit);
3414 QCOMPARE(view.activeFocusItem(), view.contentItem());
3415 anotherEdit.setFocus(true);
3416
3417 qApp->inputMethod()->hide();
3418
3419 // input panel should not be opened if TextEdit is read only
3420 edit->setReadOnly(true);
3421 edit->setFocus(true);
3422 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3423 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3424 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3425 QGuiApplication::processEvents();
3426 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3427
3428 // input panel should not be opened if focusOnPress is set to false
3429 edit->setFocusOnPress(false);
3430 edit->setFocus(false);
3431 edit->setFocus(true);
3432 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3433 QTest::mousePress(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3434 QTest::mouseRelease(window: &view, button: Qt::LeftButton, stateKey: noModifiers, pos: centerPoint);
3435 QCOMPARE(qApp->inputMethod()->isVisible(), false);
3436
3437 inputMethodPrivate->testContext = nullptr;
3438}
3439
3440void tst_qquicktextedit::geometrySignals()
3441{
3442 QQmlComponent component(&engine, testFileUrl(fileName: "geometrySignals.qml"));
3443 QObject *o = component.create();
3444 QVERIFY(o);
3445 QCOMPARE(o->property("bindingWidth").toInt(), 400);
3446 QCOMPARE(o->property("bindingHeight").toInt(), 500);
3447 delete o;
3448}
3449
3450#if QT_CONFIG(clipboard)
3451void tst_qquicktextedit::pastingRichText_QTBUG_14003()
3452{
3453 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
3454 QQmlComponent component(&engine);
3455 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
3456 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(object: component.create());
3457
3458 QTRY_VERIFY(obj != nullptr);
3459 QTRY_COMPARE(obj->textFormat(), QQuickTextEdit::PlainText);
3460
3461 QMimeData *mData = new QMimeData;
3462 mData->setHtml("<font color=\"red\">Hello</font>");
3463 QGuiApplication::clipboard()->setMimeData(data: mData);
3464
3465 obj->paste();
3466 QTRY_COMPARE(obj->text(), QString());
3467 QTRY_COMPARE(obj->textFormat(), QQuickTextEdit::PlainText);
3468}
3469#endif
3470
3471void tst_qquicktextedit::implicitSize_data()
3472{
3473 QTest::addColumn<QString>(name: "text");
3474 QTest::addColumn<QString>(name: "wrap");
3475 QTest::addColumn<QString>(name: "format");
3476 QTest::newRow(dataTag: "plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap" << "TextEdit.PlainText";
3477 QTest::newRow(dataTag: "richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap" << "TextEdit.RichText";
3478 QTest::newRow(dataTag: "plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap" << "TextEdit.PlainText";
3479 QTest::newRow(dataTag: "richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap" << "TextEdit.RichText";
3480}
3481
3482void tst_qquicktextedit::implicitSize()
3483{
3484 QFETCH(QString, text);
3485 QFETCH(QString, wrap);
3486 QFETCH(QString, format);
3487 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + "; textFormat: " + format + " }";
3488 QQmlComponent textComponent(&engine);
3489 textComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
3490 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(object: textComponent.create());
3491
3492 QVERIFY(textObject->width() < textObject->implicitWidth());
3493 QCOMPARE(textObject->height(), textObject->implicitHeight());
3494
3495 textObject->resetWidth();
3496 QCOMPARE(textObject->width(), textObject->implicitWidth());
3497 QCOMPARE(textObject->height(), textObject->implicitHeight());
3498}
3499
3500void tst_qquicktextedit::implicitSize_QTBUG_63153()
3501{
3502 QString componentStr = "import QtQuick 2.0\nTextEdit { }";
3503 QQmlComponent textComponent(&engine);
3504 textComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
3505 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(object: textComponent.create());
3506 textObject->setText("short");
3507 qreal shortImplicitWidth = textObject->implicitWidth();
3508 textObject->setText("in contrast to short this is long");
3509 QVERIFY2(shortImplicitWidth < textObject->implicitWidth(), qPrintable(QString("%1 < %2").arg(textObject->implicitWidth()).arg(shortImplicitWidth)));
3510}
3511
3512void tst_qquicktextedit::contentSize()
3513{
3514 QString componentStr = "import QtQuick 2.0\nTextEdit { width: 75; height: 16; font.pixelSize: 10 }";
3515 QQmlComponent textComponent(&engine);
3516 textComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
3517 QScopedPointer<QObject> object(textComponent.create());
3518 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object: object.data());
3519
3520 QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
3521
3522 textObject->setText("The quick red fox jumped over the lazy brown dog");
3523
3524 QVERIFY(textObject->contentWidth() > textObject->width());
3525 QVERIFY(textObject->contentHeight() < textObject->height());
3526 QCOMPARE(spy.count(), 1);
3527
3528 textObject->setWrapMode(QQuickTextEdit::WordWrap);
3529 QVERIFY(textObject->contentWidth() <= textObject->width());
3530 QVERIFY(textObject->contentHeight() > textObject->height());
3531 QCOMPARE(spy.count(), 2);
3532
3533 textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
3534
3535 QVERIFY(textObject->contentWidth() > textObject->width());
3536 QVERIFY(textObject->contentHeight() > textObject->height());
3537 QCOMPARE(spy.count(), 3);
3538}
3539
3540void tst_qquicktextedit::implicitSizeBinding_data()
3541{
3542 implicitSize_data();
3543}
3544
3545void tst_qquicktextedit::implicitSizeBinding()
3546{
3547 QFETCH(QString, text);
3548 QFETCH(QString, wrap);
3549 QFETCH(QString, format);
3550 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
3551 QQmlComponent textComponent(&engine);
3552 textComponent.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
3553 QScopedPointer<QObject> object(textComponent.create());
3554 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object: object.data());
3555
3556 QCOMPARE(textObject->width(), textObject->implicitWidth());
3557 QCOMPARE(textObject->height(), textObject->implicitHeight());
3558
3559 textObject->resetWidth();
3560 QCOMPARE(textObject->width(), textObject->implicitWidth());
3561 QCOMPARE(textObject->height(), textObject->implicitHeight());
3562
3563 textObject->resetHeight();
3564 QCOMPARE(textObject->width(), textObject->implicitWidth());
3565 QCOMPARE(textObject->height(), textObject->implicitHeight());
3566}
3567
3568void tst_qquicktextedit::signal_editingfinished()
3569{
3570 QQuickView *window = new QQuickView(nullptr);
3571 window->setBaseSize(QSize(800,600));
3572
3573 window->setSource(testFileUrl(fileName: "signal_editingfinished.qml"));
3574 window->show();
3575 window->requestActivate();
3576 QVERIFY(QTest::qWaitForWindowActive(window));
3577 QCOMPARE(QGuiApplication::focusWindow(), window);
3578
3579 QVERIFY(window->rootObject() != nullptr);
3580
3581 QQuickTextEdit *input1 = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window->rootObject()->property(name: "input1")));
3582 QVERIFY(input1);
3583 QQuickTextEdit *input2 = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window->rootObject()->property(name: "input2")));
3584 QVERIFY(input2);
3585
3586 QSignalSpy editingFinished1Spy(input1, SIGNAL(editingFinished()));
3587
3588 input1->setFocus(true);
3589 QTRY_VERIFY(input1->hasActiveFocus());
3590 QTRY_VERIFY(!input2->hasActiveFocus());
3591
3592 QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
3593 QGuiApplication::sendEvent(receiver: window, event: &key);
3594 QVERIFY(key.isAccepted());
3595 QTRY_COMPARE(editingFinished1Spy.count(), 1);
3596
3597 QTRY_VERIFY(!input1->hasActiveFocus());
3598 QTRY_VERIFY(input2->hasActiveFocus());
3599
3600 QSignalSpy editingFinished2Spy(input2, SIGNAL(editingFinished()));
3601
3602 input2->setFocus(true);
3603 QTRY_VERIFY(!input1->hasActiveFocus());
3604 QTRY_VERIFY(input2->hasActiveFocus());
3605
3606 key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
3607 QGuiApplication::sendEvent(receiver: window, event: &key);
3608 QVERIFY(key.isAccepted());
3609 QTRY_COMPARE(editingFinished2Spy.count(), 1);
3610
3611 QTRY_VERIFY(input1->hasActiveFocus());
3612 QTRY_VERIFY(!input2->hasActiveFocus());
3613}
3614
3615void tst_qquicktextedit::clipRect()
3616{
3617 QQmlComponent component(&engine);
3618 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
3619 QScopedPointer<QObject> object(component.create());
3620 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
3621 QVERIFY(edit);
3622
3623 QCOMPARE(edit->clipRect().x(), qreal(0));
3624 QCOMPARE(edit->clipRect().y(), qreal(0));
3625
3626 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width());
3627 QCOMPARE(edit->clipRect().height(), edit->height());
3628
3629 edit->setText("Hello World");
3630 QCOMPARE(edit->clipRect().x(), qreal(0));
3631 QCOMPARE(edit->clipRect().y(), qreal(0));
3632 // XXX: TextEdit allows an extra 3 pixels boundary for the cursor beyond it's width for non
3633 // empty text. TextInput doesn't.
3634 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
3635 QCOMPARE(edit->clipRect().height(), edit->height());
3636
3637 // clip rect shouldn't exceed the size of the item, expect for the cursor width;
3638 edit->setWidth(edit->width() / 2);
3639 QCOMPARE(edit->clipRect().x(), qreal(0));
3640 QCOMPARE(edit->clipRect().y(), qreal(0));
3641 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
3642 QCOMPARE(edit->clipRect().height(), edit->height());
3643
3644 edit->setHeight(edit->height() * 2);
3645 QCOMPARE(edit->clipRect().x(), qreal(0));
3646 QCOMPARE(edit->clipRect().y(), qreal(0));
3647 QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
3648 QCOMPARE(edit->clipRect().height(), edit->height());
3649
3650 QQmlComponent cursorComponent(&engine);
3651 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", baseUrl: QUrl());
3652
3653 edit->setCursorDelegate(&cursorComponent);
3654 edit->setCursorVisible(true);
3655
3656 // If a cursor delegate is used it's size should determine the excess width.
3657 QCOMPARE(edit->clipRect().x(), qreal(0));
3658 QCOMPARE(edit->clipRect().y(), qreal(0));
3659 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
3660 QCOMPARE(edit->clipRect().height(), edit->height());
3661
3662 // Alignment and wrapping don't affect the clip rect.
3663 edit->setHAlign(QQuickTextEdit::AlignRight);
3664 QCOMPARE(edit->clipRect().x(), qreal(0));
3665 QCOMPARE(edit->clipRect().y(), qreal(0));
3666 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
3667 QCOMPARE(edit->clipRect().height(), edit->height());
3668
3669 edit->setWrapMode(QQuickTextEdit::Wrap);
3670 QCOMPARE(edit->clipRect().x(), qreal(0));
3671 QCOMPARE(edit->clipRect().y(), qreal(0));
3672 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
3673 QCOMPARE(edit->clipRect().height(), edit->height());
3674
3675 edit->setVAlign(QQuickTextEdit::AlignBottom);
3676 QCOMPARE(edit->clipRect().x(), qreal(0));
3677 QCOMPARE(edit->clipRect().y(), qreal(0));
3678 QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
3679 QCOMPARE(edit->clipRect().height(), edit->height());
3680}
3681
3682void tst_qquicktextedit::boundingRect()
3683{
3684 QQmlComponent component(&engine);
3685 component.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: QUrl());
3686 QScopedPointer<QObject> object(component.create());
3687 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: object.data());
3688 QVERIFY(edit);
3689
3690 QTextLayout layout;
3691 layout.setFont(edit->font());
3692
3693 if (!qmlDisableDistanceField()) {
3694 QTextOption option;
3695 option.setUseDesignMetrics(true);
3696 layout.setTextOption(option);
3697 }
3698 layout.beginLayout();
3699 QTextLine line = layout.createLine();
3700 layout.endLayout();
3701
3702 QCOMPARE(edit->boundingRect().x(), qreal(0));
3703 QCOMPARE(edit->boundingRect().y(), qreal(0));
3704 QCOMPARE(edit->boundingRect().width(), edit->cursorRectangle().width());
3705 QCOMPARE(edit->boundingRect().height(), line.height());
3706
3707 edit->setText("Hello World");
3708
3709 layout.setText(edit->text());
3710 layout.beginLayout();
3711 line = layout.createLine();
3712 layout.endLayout();
3713
3714 QCOMPARE(edit->boundingRect().x(), qreal(0));
3715 QCOMPARE(edit->boundingRect().y(), qreal(0));
3716 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
3717 QCOMPARE(edit->boundingRect().height(), line.height());
3718
3719 // the size of the bounding rect shouldn't be bounded by the size of item.
3720 edit->setWidth(edit->width() / 2);
3721 QCOMPARE(edit->boundingRect().x(), qreal(0));
3722 QCOMPARE(edit->boundingRect().y(), qreal(0));
3723 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
3724 QCOMPARE(edit->boundingRect().height(), line.height());
3725
3726 edit->setHeight(edit->height() * 2);
3727 QCOMPARE(edit->boundingRect().x(), qreal(0));
3728 QCOMPARE(edit->boundingRect().y(), qreal(0));
3729 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
3730 QCOMPARE(edit->boundingRect().height(), line.height());
3731
3732 QQmlComponent cursorComponent(&engine);
3733 cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", baseUrl: QUrl());
3734
3735 edit->setCursorDelegate(&cursorComponent);
3736 edit->setCursorVisible(true);
3737
3738 // Don't include the size of a cursor delegate as it has its own bounding rect.
3739 QCOMPARE(edit->boundingRect().x(), qreal(0));
3740 QCOMPARE(edit->boundingRect().y(), qreal(0));
3741 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
3742 QCOMPARE(edit->boundingRect().height(), line.height());
3743
3744 edit->setHAlign(QQuickTextEdit::AlignRight);
3745 QCOMPARE(edit->boundingRect().x(), edit->width() - line.naturalTextWidth());
3746 QCOMPARE(edit->boundingRect().y(), qreal(0));
3747 QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
3748 QCOMPARE(edit->boundingRect().height(), line.height());
3749
3750 edit->setWrapMode(QQuickTextEdit::Wrap);
3751 QCOMPARE(edit->boundingRect().right(), edit->width());
3752 QCOMPARE(edit->boundingRect().y(), qreal(0));
3753 QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
3754 QVERIFY(edit->boundingRect().height() > line.height());
3755
3756 edit->setVAlign(QQuickTextEdit::AlignBottom);
3757 QCOMPARE(edit->boundingRect().right(), edit->width());
3758 QCOMPARE(edit->boundingRect().bottom(), edit->height());
3759 QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
3760 QVERIFY(edit->boundingRect().height() > line.height());
3761}
3762
3763void tst_qquicktextedit::preeditCursorRectangle()
3764{
3765 QString preeditText = "super";
3766
3767 QQuickView view(testFileUrl(fileName: "inputMethodEvent.qml"));
3768 view.show();
3769 view.requestActivate();
3770 QVERIFY(QTest::qWaitForWindowActive(&view));
3771
3772 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: view.rootObject());
3773 QVERIFY(edit);
3774
3775 QQuickItem *cursor = edit->findChild<QQuickItem *>(aName: "cursor");
3776 QVERIFY(cursor);
3777
3778 QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
3779 QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3780
3781 QRectF currentRect;
3782
3783 QCOMPARE(QGuiApplication::focusObject(), static_cast<QObject *>(edit));
3784 QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3785 QCoreApplication::sendEvent(receiver: edit, event: &query);
3786 QRectF previousRect = query.value(query: Qt::ImCursorRectangle).toRectF();
3787
3788 // Verify that the micro focus rect is positioned the same for position 0 as
3789 // it would be if there was no preedit text.
3790 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
3791 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, preeditText.length(), QVariant()));
3792 QCoreApplication::sendEvent(receiver: edit, event: &imEvent);
3793 QCoreApplication::sendEvent(receiver: edit, event: &query);
3794 currentRect = query.value(query: Qt::ImCursorRectangle).toRectF();
3795 QCOMPARE(edit->cursorRectangle(), currentRect);
3796 QCOMPARE(cursor->position(), currentRect.topLeft());
3797 QCOMPARE(currentRect, previousRect);
3798
3799 // Verify that the micro focus rect moves to the left as the cursor position
3800 // is incremented.
3801 editSpy.clear();
3802 panelSpy.clear();
3803 for (int i = 1; i <= 5; ++i) {
3804 QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
3805 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, preeditText.length(), QVariant()));
3806 QCoreApplication::sendEvent(receiver: edit, event: &imEvent);
3807 QCoreApplication::sendEvent(receiver: edit, event: &query);
3808 currentRect = query.value(query: Qt::ImCursorRectangle).toRectF();
3809 QCOMPARE(edit->cursorRectangle(), currentRect);
3810 QCOMPARE(cursor->position(), currentRect.topLeft());
3811 QVERIFY(previousRect.left() < currentRect.left());
3812 QCOMPARE(editSpy.count(), 1); editSpy.clear();
3813 QCOMPARE(panelSpy.count(), 1); panelSpy.clear();
3814 previousRect = currentRect;
3815 }
3816
3817 // Verify that if the cursor rectangle is updated if the pre-edit text changes
3818 // but the (non-zero) cursor position is the same.
3819 editSpy.clear();
3820 panelSpy.clear();
3821 { QInputMethodEvent imEvent("wwwww", QList<QInputMethodEvent::Attribute>()
3822 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 5, 1, QVariant()));
3823 QCoreApplication::sendEvent(receiver: edit, event: &imEvent); }
3824 QCoreApplication::sendEvent(receiver: edit, event: &query);
3825 currentRect = query.value(query: Qt::ImCursorRectangle).toRectF();
3826 QCOMPARE(edit->cursorRectangle(), currentRect);
3827 QCOMPARE(cursor->position(), currentRect.topLeft());
3828 QCOMPARE(editSpy.count(), 1);
3829 QCOMPARE(panelSpy.count(), 1);
3830
3831 // Verify that if there is no preedit cursor then the micro focus rect is the
3832 // same as it would be if it were positioned at the end of the preedit text.
3833 editSpy.clear();
3834 panelSpy.clear();
3835 { QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3836 QCoreApplication::sendEvent(receiver: edit, event: &imEvent); }
3837 QCoreApplication::sendEvent(receiver: edit, event: &query);
3838 currentRect = query.value(query: Qt::ImCursorRectangle).toRectF();
3839 QCOMPARE(edit->cursorRectangle(), currentRect);
3840 QCOMPARE(cursor->position(), currentRect.topLeft());
3841 QCOMPARE(currentRect, previousRect);
3842 QCOMPARE(editSpy.count(), 1);
3843 QCOMPARE(panelSpy.count(), 1);
3844}
3845
3846void tst_qquicktextedit::inputMethodComposing()
3847{
3848 QString text = "supercalifragisiticexpialidocious!";
3849
3850 QQuickView view(testFileUrl(fileName: "inputContext.qml"));
3851 view.show();
3852 view.requestActivate();
3853 QVERIFY(QTest::qWaitForWindowActive(&view));
3854
3855 QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object: view.rootObject());
3856 QVERIFY(edit);
3857 QCOMPARE(QGuiApplication::focusObject(), static_cast<QObject *>(edit));
3858
3859 QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
3860 edit->setCursorPosition(12);
3861
3862 QCOMPARE(edit->isInputMethodComposing(), false);
3863
3864 {
3865 QInputMethodEvent event(text.mid(position: 3), QList<QInputMethodEvent::Attribute>());
3866 QGuiApplication::sendEvent(receiver: edit, event: &event);
3867 }
3868
3869 QCOMPARE(edit->isInputMethodComposing(), true);
3870 QCOMPARE(spy.count(), 1);
3871
3872 {
3873 QInputMethodEvent event(text.mid(position: 12), QList<QInputMethodEvent::Attribute>());
3874 QGuiApplication::sendEvent(receiver: edit, event: &event);
3875 }
3876 QCOMPARE(spy.count(), 1);
3877
3878 {
3879 QInputMethodEvent event;
3880 QGuiApplication::sendEvent(receiver: edit, event: &event);
3881 }
3882 QCOMPARE(edit->isInputMethodComposing(), false);
3883 QCOMPARE(spy.count(), 2);
3884
3885 // Changing the text while not composing doesn't alter the composing state.
3886 edit->setText(text.mid(position: 0, n: 16));
3887 QCOMPARE(edit->isInputMethodComposing(), false);
3888 QCOMPARE(spy.count(), 2);
3889
3890 {
3891 QInputMethodEvent event(text.mid(position: 16), QList<QInputMethodEvent::Attribute>());
3892 QGuiApplication::sendEvent(receiver: edit, event: &event);
3893 }
3894 QCOMPARE(edit->isInputMethodComposing(), true);
3895 QCOMPARE(spy.count(), 3);
3896
3897 // Changing the text while composing cancels composition.
3898 edit->setText(text.mid(position: 0, n: 12));
3899 QCOMPARE(edit->isInputMethodComposing(), false);
3900 QCOMPARE(spy.count(), 4);
3901
3902 { // Preedit cursor positioned outside (empty) preedit; composing.
3903 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3904 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3905 QGuiApplication::sendEvent(receiver: edit, event: &event);
3906 }
3907 QCOMPARE(edit->isInputMethodComposing(), true);
3908 QCOMPARE(spy.count(), 5);
3909
3910 { // Cursor hidden; composing
3911 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3912 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3913 QGuiApplication::sendEvent(receiver: edit, event: &event);
3914 }
3915 QCOMPARE(edit->isInputMethodComposing(), true);
3916 QCOMPARE(spy.count(), 5);
3917
3918 { // Default cursor attributes; composing.
3919 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3920 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3921 QGuiApplication::sendEvent(receiver: edit, event: &event);
3922 }
3923 QCOMPARE(edit->isInputMethodComposing(), true);
3924 QCOMPARE(spy.count(), 5);
3925
3926 { // Selections are persisted: not composing
3927 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3928 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 2, 4, QVariant()));
3929 QGuiApplication::sendEvent(receiver: edit, event: &event);
3930 }
3931 QCOMPARE(edit->isInputMethodComposing(), false);
3932 QCOMPARE(spy.count(), 6);
3933
3934 edit->setCursorPosition(0);
3935
3936 { // Formatting applied; composing.
3937 QTextCharFormat format;
3938 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3939 QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3940 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 2, 4, format));
3941 QGuiApplication::sendEvent(receiver: edit, event: &event);
3942 }
3943 QCOMPARE(edit->isInputMethodComposing(), true);
3944 QCOMPARE(spy.count(), 7);
3945
3946 {
3947 QInputMethodEvent event;
3948 QGuiApplication::sendEvent(receiver: edit, event: &event);
3949 }
3950 QCOMPARE(edit->isInputMethodComposing(), false);
3951 QCOMPARE(spy.count(), 8);
3952}
3953
3954void tst_qquicktextedit::cursorRectangleSize_data()
3955{
3956 QTest::addColumn<bool>(name: "useCursorDelegate");
3957
3958 QTest::newRow(dataTag: "default cursor") << false;
3959 QTest::newRow(dataTag: "custom cursor delegate") << true;
3960}
3961
3962void tst_qquicktextedit::cursorRectangleSize()
3963{
3964 QFETCH(bool, useCursorDelegate);
3965
3966 QQuickView *window = new QQuickView(testFileUrl(fileName: "positionAt.qml"));
3967 QVERIFY(window->rootObject() != nullptr);
3968 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(object: window->rootObject());
3969
3970 QQmlComponent cursorDelegate(window->engine());
3971 if (useCursorDelegate) {
3972 cursorDelegate.setData("import QtQuick 2.0\nRectangle { width:10; height:10; }", baseUrl: QUrl());
3973 textEdit->setCursorDelegate(&cursorDelegate);
3974 }
3975
3976 // make sure cursor rectangle is not at (0,0)
3977 textEdit->setX(10);
3978 textEdit->setY(10);
3979 textEdit->setCursorPosition(3);
3980 QVERIFY(textEdit != nullptr);
3981 textEdit->setFocus(true);
3982 window->show();
3983 window->requestActivate();
3984 QVERIFY(QTest::qWaitForWindowActive(window));
3985
3986 QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3987 qApp->sendEvent(receiver: textEdit, event: &event);
3988 QRectF cursorRectFromQuery = event.value(query: Qt::ImCursorRectangle).toRectF();
3989
3990 QRectF cursorRectFromItem = textEdit->cursorRectangle();
3991 QRectF cursorRectFromPositionToRectangle = textEdit->positionToRectangle(textEdit->cursorPosition());
3992
3993 QVERIFY(cursorRectFromItem.isValid());
3994 QVERIFY(cursorRectFromQuery.isValid());
3995 QVERIFY(cursorRectFromPositionToRectangle.isValid());
3996
3997 // item and input query cursor rectangles match
3998 QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3999
4000 // item cursor rectangle and positionToRectangle calculations match
4001 QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
4002
4003 // item-window transform and input item transform match
4004 QCOMPARE(QQuickItemPrivate::get(textEdit)->itemToWindowTransform(), qApp->inputMethod()->inputItemTransform());
4005
4006 // input panel cursorRectangle property and tranformed item cursor rectangle match
4007 QRectF sceneCursorRect = QQuickItemPrivate::get(item: textEdit)->itemToWindowTransform().mapRect(cursorRectFromItem);
4008 QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
4009
4010 delete window;
4011}
4012
4013void tst_qquicktextedit::getText_data()
4014{
4015 QTest::addColumn<QString>(name: "text");
4016 QTest::addColumn<int>(name: "start");
4017 QTest::addColumn<int>(name: "end");
4018 QTest::addColumn<QString>(name: "expectedText");
4019
4020 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
4021 const QString plainBoldText = QStringLiteral("This is some bold text");
4022 const QString richBoldTextLB = QStringLiteral("This is some<br/><b>bold</b> text");
4023 const QString plainBoldTextLB = QString(QStringLiteral("This is some\nbold text")).replace(before: QLatin1Char('\n'), after: QChar(QChar::LineSeparator));
4024
4025 QTest::newRow(dataTag: "all plain text")
4026 << standard.at(i: 0)
4027 << 0 << standard.at(i: 0).length()
4028 << standard.at(i: 0);
4029
4030 QTest::newRow(dataTag: "plain text sub string")
4031 << standard.at(i: 0)
4032 << 0 << 12
4033 << standard.at(i: 0).mid(position: 0, n: 12);
4034
4035 QTest::newRow(dataTag: "plain text sub string reversed")
4036 << standard.at(i: 0)
4037 << 12 << 0
4038 << standard.at(i: 0).mid(position: 0, n: 12);
4039
4040 QTest::newRow(dataTag: "plain text cropped beginning")
4041 << standard.at(i: 0)
4042 << -3 << 4
4043 << standard.at(i: 0).mid(position: 0, n: 4);
4044
4045 QTest::newRow(dataTag: "plain text cropped end")
4046 << standard.at(i: 0)
4047 << 23 << standard.at(i: 0).length() + 8
4048 << standard.at(i: 0).mid(position: 23);
4049
4050 QTest::newRow(dataTag: "plain text cropped beginning and end")
4051 << standard.at(i: 0)
4052 << -9 << standard.at(i: 0).length() + 4
4053 << standard.at(i: 0);
4054
4055 QTest::newRow(dataTag: "all rich text")
4056 << richBoldText
4057 << 0 << plainBoldText.length()
4058 << plainBoldText;
4059
4060 QTest::newRow(dataTag: "rich text sub string")
4061 << richBoldText
4062 << 14 << 21
4063 << plainBoldText.mid(position: 14, n: 7);
4064
4065 // Line break.
4066 QTest::newRow(dataTag: "all plain text (line break)")
4067 << standard.at(i: 1)
4068 << 0 << standard.at(i: 1).length()
4069 << standard.at(i: 1);
4070
4071 QTest::newRow(dataTag: "plain text sub string (line break)")
4072 << standard.at(i: 1)
4073 << 0 << 12
4074 << standard.at(i: 1).mid(position: 0, n: 12);
4075
4076 QTest::newRow(dataTag: "plain text sub string reversed (line break)")
4077 << standard.at(i: 1)
4078 << 12 << 0
4079 << standard.at(i: 1).mid(position: 0, n: 12);
4080
4081 QTest::newRow(dataTag: "plain text cropped beginning (line break)")
4082 << standard.at(i: 1)
4083 << -3 << 4
4084 << standard.at(i: 1).mid(position: 0, n: 4);
4085
4086 QTest::newRow(dataTag: "plain text cropped end (line break)")
4087 << standard.at(i: 1)
4088 << 23 << standard.at(i: 1).length() + 8
4089 << standard.at(i: 1).mid(position: 23);
4090
4091 QTest::newRow(dataTag: "plain text cropped beginning and end (line break)")
4092 << standard.at(i: 1)
4093 << -9 << standard.at(i: 1).length() + 4
4094 << standard.at(i: 1);
4095
4096 QTest::newRow(dataTag: "all rich text (line break)")
4097 << richBoldTextLB
4098 << 0 << plainBoldTextLB.length()
4099 << plainBoldTextLB;
4100
4101 QTest::newRow(dataTag: "rich text sub string (line break)")
4102 << richBoldTextLB
4103 << 14 << 21
4104 << plainBoldTextLB.mid(position: 14, n: 7);
4105}
4106
4107void tst_qquicktextedit::getText()
4108{
4109 QFETCH(QString, text);
4110 QFETCH(int, start);
4111 QFETCH(int, end);
4112 QFETCH(QString, expectedText);
4113
4114 QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + text + "\" }";
4115 QQmlComponent textEditComponent(&engine);
4116 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4117 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4118 QVERIFY(textEdit != nullptr);
4119
4120 QCOMPARE(textEdit->getText(start, end), expectedText);
4121}
4122
4123void tst_qquicktextedit::getFormattedText_data()
4124{
4125 QTest::addColumn<QString>(name: "text");
4126 QTest::addColumn<QQuickTextEdit::TextFormat>(name: "textFormat");
4127 QTest::addColumn<int>(name: "start");
4128 QTest::addColumn<int>(name: "end");
4129 QTest::addColumn<QString>(name: "expectedText");
4130
4131 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
4132 const QString plainBoldText = QStringLiteral("This is some bold text");
4133
4134 QTest::newRow(dataTag: "all plain text")
4135 << standard.at(i: 0)
4136 << QQuickTextEdit::PlainText
4137 << 0 << standard.at(i: 0).length()
4138 << standard.at(i: 0);
4139
4140 QTest::newRow(dataTag: "plain text sub string")
4141 << standard.at(i: 0)
4142 << QQuickTextEdit::PlainText
4143 << 0 << 12
4144 << standard.at(i: 0).mid(position: 0, n: 12);
4145
4146 QTest::newRow(dataTag: "plain text sub string reversed")
4147 << standard.at(i: 0)
4148 << QQuickTextEdit::PlainText
4149 << 12 << 0
4150 << standard.at(i: 0).mid(position: 0, n: 12);
4151
4152 QTest::newRow(dataTag: "plain text cropped beginning")
4153 << standard.at(i: 0)
4154 << QQuickTextEdit::PlainText
4155 << -3 << 4
4156 << standard.at(i: 0).mid(position: 0, n: 4);
4157
4158 QTest::newRow(dataTag: "plain text cropped end")
4159 << standard.at(i: 0)
4160 << QQuickTextEdit::PlainText
4161 << 23 << standard.at(i: 0).length() + 8
4162 << standard.at(i: 0).mid(position: 23);
4163
4164 QTest::newRow(dataTag: "plain text cropped beginning and end")
4165 << standard.at(i: 0)
4166 << QQuickTextEdit::PlainText
4167 << -9 << standard.at(i: 0).length() + 4
4168 << standard.at(i: 0);
4169
4170 QTest::newRow(dataTag: "all rich (Auto) text")
4171 << richBoldText
4172 << QQuickTextEdit::AutoText
4173 << 0 << plainBoldText.length()
4174 << QString("This is some \\<.*\\>bold\\</.*\\> text");
4175
4176 QTest::newRow(dataTag: "all rich (Rich) text")
4177 << richBoldText
4178 << QQuickTextEdit::RichText
4179 << 0 << plainBoldText.length()
4180 << QString("This is some \\<.*\\>bold\\</.*\\> text");
4181
4182 QTest::newRow(dataTag: "all rich (Plain) text")
4183 << richBoldText
4184 << QQuickTextEdit::PlainText
4185 << 0 << richBoldText.length()
4186 << richBoldText;
4187
4188 QTest::newRow(dataTag: "rich (Auto) text sub string")
4189 << richBoldText
4190 << QQuickTextEdit::AutoText
4191 << 14 << 21
4192 << QString("\\<.*\\>old\\</.*\\> tex");
4193
4194 QTest::newRow(dataTag: "rich (Rich) text sub string")
4195 << richBoldText
4196 << QQuickTextEdit::RichText
4197 << 14 << 21
4198 << QString("\\<.*\\>old\\</.*\\> tex");
4199
4200 QTest::newRow(dataTag: "rich (Plain) text sub string")
4201 << richBoldText
4202 << QQuickTextEdit::PlainText
4203 << 17 << 27
4204 << richBoldText.mid(position: 17, n: 10);
4205}
4206
4207void tst_qquicktextedit::getFormattedText()
4208{
4209 QFETCH(QString, text);
4210 QFETCH(QQuickTextEdit::TextFormat, textFormat);
4211 QFETCH(int, start);
4212 QFETCH(int, end);
4213 QFETCH(QString, expectedText);
4214
4215 QString componentStr = "import QtQuick 2.0\nTextEdit {}";
4216 QQmlComponent textEditComponent(&engine);
4217 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4218 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4219 QVERIFY(textEdit != nullptr);
4220
4221 textEdit->setTextFormat(textFormat);
4222 textEdit->setText(text);
4223
4224 if (textFormat == QQuickTextEdit::RichText
4225 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
4226 QVERIFY(textEdit->getFormattedText(start, end).contains(QRegExp(expectedText)));
4227 } else {
4228 QCOMPARE(textEdit->getFormattedText(start, end), expectedText);
4229 }
4230}
4231
4232void tst_qquicktextedit::append_data()
4233{
4234 QTest::addColumn<QString>(name: "text");
4235 QTest::addColumn<QQuickTextEdit::TextFormat>(name: "textFormat");
4236 QTest::addColumn<int>(name: "selectionStart");
4237 QTest::addColumn<int>(name: "selectionEnd");
4238 QTest::addColumn<QString>(name: "appendText");
4239 QTest::addColumn<QString>(name: "expectedText");
4240 QTest::addColumn<int>(name: "expectedSelectionStart");
4241 QTest::addColumn<int>(name: "expectedSelectionEnd");
4242 QTest::addColumn<int>(name: "expectedCursorPosition");
4243 QTest::addColumn<bool>(name: "selectionChanged");
4244 QTest::addColumn<bool>(name: "cursorPositionChanged");
4245
4246 QTest::newRow(dataTag: "cursor kept intact (beginning)")
4247 << standard.at(i: 0) << QQuickTextEdit::PlainText
4248 << 0 << 0
4249 << QString("Hello")
4250 << standard.at(i: 0) + QString("\nHello")
4251 << 0 << 0 << 0
4252 << false << false;
4253
4254 QTest::newRow(dataTag: "cursor kept intact (middle)")
4255 << standard.at(i: 0) << QQuickTextEdit::PlainText
4256 << 18 << 18
4257 << QString("Hello")
4258 << standard.at(i: 0) + QString("\nHello")
4259 << 18 << 18 << 18
4260 << false << false;
4261
4262 QTest::newRow(dataTag: "cursor follows (end)")
4263 << standard.at(i: 0) << QQuickTextEdit::PlainText
4264 << standard.at(i: 0).length() << standard.at(i: 0).length()
4265 << QString("Hello")
4266 << standard.at(i: 0) + QString("\nHello")
4267 << standard.at(i: 0).length() + 6 << standard.at(i: 0).length() + 6 << standard.at(i: 0).length() + 6
4268 << false << true;
4269
4270 QTest::newRow(dataTag: "selection kept intact (beginning)")
4271 << standard.at(i: 0) << QQuickTextEdit::PlainText
4272 << 0 << 18
4273 << QString("Hello")
4274 << standard.at(i: 0) + QString("\nHello")
4275 << 0 << 18 << 18
4276 << false << false;
4277
4278 QTest::newRow(dataTag: "selection kept intact (middle)")
4279 << standard.at(i: 0) << QQuickTextEdit::PlainText
4280 << 14 << 18
4281 << QString("Hello")
4282 << standard.at(i: 0) + QString("\nHello")
4283 << 14 << 18 << 18
4284 << false << false;
4285
4286 QTest::newRow(dataTag: "selection kept intact, cursor follows (end)")
4287 << standard.at(i: 0) << QQuickTextEdit::PlainText
4288 << 18 << standard.at(i: 0).length()
4289 << QString("Hello")
4290 << standard.at(i: 0) + QString("\nHello")
4291 << 18 << standard.at(i: 0).length() + 6 << standard.at(i: 0).length() + 6
4292 << true << true;
4293
4294 QTest::newRow(dataTag: "reversed selection kept intact")
4295 << standard.at(i: 0) << QQuickTextEdit::PlainText
4296 << 18 << 14
4297 << QString("Hello")
4298 << standard.at(i: 0) + QString("\nHello")
4299 << 14 << 18 << 14
4300 << false << false;
4301
4302 QTest::newRow(dataTag: "rich text into plain text")
4303 << standard.at(i: 0) << QQuickTextEdit::PlainText
4304 << 0 << 0
4305 << QString("<b>Hello</b>")
4306 << standard.at(i: 0) + QString("\n<b>Hello</b>")
4307 << 0 << 0 << 0
4308 << false << false;
4309
4310 QTest::newRow(dataTag: "rich text into rich text")
4311 << standard.at(i: 0) << QQuickTextEdit::RichText
4312 << 0 << 0
4313 << QString("<b>Hello</b>")
4314 << standard.at(i: 0) + QChar(QChar::ParagraphSeparator) + QString("Hello")
4315 << 0 << 0 << 0
4316 << false << false;
4317
4318 QTest::newRow(dataTag: "rich text into auto text")
4319 << standard.at(i: 0) << QQuickTextEdit::AutoText
4320 << 0 << 0
4321 << QString("<b>Hello</b>")
4322 << standard.at(i: 0) + QString("\nHello")
4323 << 0 << 0 << 0
4324 << false << false;
4325}
4326
4327void tst_qquicktextedit::append()
4328{
4329 QFETCH(QString, text);
4330 QFETCH(QQuickTextEdit::TextFormat, textFormat);
4331 QFETCH(int, selectionStart);
4332 QFETCH(int, selectionEnd);
4333 QFETCH(QString, appendText);
4334 QFETCH(QString, expectedText);
4335 QFETCH(int, expectedSelectionStart);
4336 QFETCH(int, expectedSelectionEnd);
4337 QFETCH(int, expectedCursorPosition);
4338 QFETCH(bool, selectionChanged);
4339 QFETCH(bool, cursorPositionChanged);
4340
4341 QString componentStr = "import QtQuick 2.2\nTextEdit { text: \"" + text + "\" }";
4342 QQmlComponent textEditComponent(&engine);
4343 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4344 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4345 QVERIFY(textEdit != nullptr);
4346
4347 textEdit->setTextFormat(textFormat);
4348 textEdit->select(start: selectionStart, end: selectionEnd);
4349
4350 QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
4351 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
4352 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
4353 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
4354 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
4355
4356 textEdit->append(text: appendText);
4357
4358 if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
4359 Qt::mightBeRichText(text) || Qt::mightBeRichText(appendText)))) {
4360 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
4361 } else {
4362 QCOMPARE(textEdit->text(), expectedText);
4363
4364 }
4365 QCOMPARE(textEdit->length(), expectedText.length());
4366
4367 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
4368 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
4369 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
4370
4371 if (selectionStart > selectionEnd)
4372 qSwap(value1&: selectionStart, value2&: selectionEnd);
4373
4374 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4375 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4376 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4377 QCOMPARE(textSpy.count() > 0, text != expectedText);
4378 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4379}
4380
4381void tst_qquicktextedit::insert_data()
4382{
4383 QTest::addColumn<QString>(name: "text");
4384 QTest::addColumn<QQuickTextEdit::TextFormat>(name: "textFormat");
4385 QTest::addColumn<int>(name: "selectionStart");
4386 QTest::addColumn<int>(name: "selectionEnd");
4387 QTest::addColumn<int>(name: "insertPosition");
4388 QTest::addColumn<QString>(name: "insertText");
4389 QTest::addColumn<QString>(name: "expectedText");
4390 QTest::addColumn<int>(name: "expectedSelectionStart");
4391 QTest::addColumn<int>(name: "expectedSelectionEnd");
4392 QTest::addColumn<int>(name: "expectedCursorPosition");
4393 QTest::addColumn<bool>(name: "selectionChanged");
4394 QTest::addColumn<bool>(name: "cursorPositionChanged");
4395
4396 QTest::newRow(dataTag: "at cursor position (beginning)")
4397 << standard.at(i: 0) << QQuickTextEdit::PlainText
4398 << 0 << 0 << 0
4399 << QString("Hello")
4400 << QString("Hello") + standard.at(i: 0)
4401 << 5 << 5 << 5
4402 << false << true;
4403
4404 QTest::newRow(dataTag: "at cursor position (end)")
4405 << standard.at(i: 0) << QQuickTextEdit::PlainText
4406 << standard.at(i: 0).length() << standard.at(i: 0).length() << standard.at(i: 0).length()
4407 << QString("Hello")
4408 << standard.at(i: 0) + QString("Hello")
4409 << standard.at(i: 0).length() + 5 << standard.at(i: 0).length() + 5 << standard.at(i: 0).length() + 5
4410 << false << true;
4411
4412 QTest::newRow(dataTag: "at cursor position (middle)")
4413 << standard.at(i: 0) << QQuickTextEdit::PlainText
4414 << 18 << 18 << 18
4415 << QString("Hello")
4416 << standard.at(i: 0).mid(position: 0, n: 18) + QString("Hello") + standard.at(i: 0).mid(position: 18)
4417 << 23 << 23 << 23
4418 << false << true;
4419
4420 QTest::newRow(dataTag: "after cursor position (beginning)")
4421 << standard.at(i: 0) << QQuickTextEdit::PlainText
4422 << 0 << 0 << 18
4423 << QString("Hello")
4424 << standard.at(i: 0).mid(position: 0, n: 18) + QString("Hello") + standard.at(i: 0).mid(position: 18)
4425 << 0 << 0 << 0
4426 << false << false;
4427
4428 QTest::newRow(dataTag: "before cursor position (end)")
4429 << standard.at(i: 0) << QQuickTextEdit::PlainText
4430 << standard.at(i: 0).length() << standard.at(i: 0).length() << 18
4431 << QString("Hello")
4432 << standard.at(i: 0).mid(position: 0, n: 18) + QString("Hello") + standard.at(i: 0).mid(position: 18)
4433 << standard.at(i: 0).length() + 5 << standard.at(i: 0).length() + 5 << standard.at(i: 0).length() + 5
4434 << false << true;
4435
4436 QTest::newRow(dataTag: "before cursor position (middle)")
4437 << standard.at(i: 0) << QQuickTextEdit::PlainText
4438 << 18 << 18 << 0
4439 << QString("Hello")
4440 << QString("Hello") + standard.at(i: 0)
4441 << 23 << 23 << 23
4442 << false << true;
4443
4444 QTest::newRow(dataTag: "after cursor position (middle)")
4445 << standard.at(i: 0) << QQuickTextEdit::PlainText
4446 << 18 << 18 << standard.at(i: 0).length()
4447 << QString("Hello")
4448 << standard.at(i: 0) + QString("Hello")
4449 << 18 << 18 << 18
4450 << false << false;
4451
4452 QTest::newRow(dataTag: "before selection")
4453 << standard.at(i: 0) << QQuickTextEdit::PlainText
4454 << 14 << 19 << 0
4455 << QString("Hello")
4456 << QString("Hello") + standard.at(i: 0)
4457 << 19 << 24 << 24
4458 << true << true;
4459
4460 QTest::newRow(dataTag: "before reversed selection")
4461 << standard.at(i: 0) << QQuickTextEdit::PlainText
4462 << 19 << 14 << 0
4463 << QString("Hello")
4464 << QString("Hello") + standard.at(i: 0)
4465 << 19 << 24 << 19
4466 << true << true;
4467
4468 QTest::newRow(dataTag: "after selection")
4469 << standard.at(i: 0) << QQuickTextEdit::PlainText
4470 << 14 << 19 << standard.at(i: 0).length()
4471 << QString("Hello")
4472 << standard.at(i: 0) + QString("Hello")
4473 << 14 << 19 << 19
4474 << false << false;
4475
4476 QTest::newRow(dataTag: "after reversed selection")
4477 << standard.at(i: 0) << QQuickTextEdit::PlainText
4478 << 19 << 14 << standard.at(i: 0).length()
4479 << QString("Hello")
4480 << standard.at(i: 0) + QString("Hello")
4481 << 14 << 19 << 14
4482 << false << false;
4483
4484 QTest::newRow(dataTag: "into selection")
4485 << standard.at(i: 0) << QQuickTextEdit::PlainText
4486 << 14 << 19 << 18
4487 << QString("Hello")
4488 << standard.at(i: 0).mid(position: 0, n: 18) + QString("Hello") + standard.at(i: 0).mid(position: 18)
4489 << 14 << 24 << 24
4490 << true << true;
4491
4492 QTest::newRow(dataTag: "into reversed selection")
4493 << standard.at(i: 0) << QQuickTextEdit::PlainText
4494 << 19 << 14 << 18
4495 << QString("Hello")
4496 << standard.at(i: 0).mid(position: 0, n: 18) + QString("Hello") + standard.at(i: 0).mid(position: 18)
4497 << 14 << 24 << 14
4498 << true << false;
4499
4500 QTest::newRow(dataTag: "rich text into plain text")
4501 << standard.at(i: 0) << QQuickTextEdit::PlainText
4502 << 0 << 0 << 0
4503 << QString("<b>Hello</b>")
4504 << QString("<b>Hello</b>") + standard.at(i: 0)
4505 << 12 << 12 << 12
4506 << false << true;
4507
4508 QTest::newRow(dataTag: "rich text into rich text")
4509 << standard.at(i: 0) << QQuickTextEdit::RichText
4510 << 0 << 0 << 0
4511 << QString("<b>Hello</b>")
4512 << QString("Hello") + standard.at(i: 0)
4513 << 5 << 5 << 5
4514 << false << true;
4515
4516 QTest::newRow(dataTag: "rich text into auto text")
4517 << standard.at(i: 0) << QQuickTextEdit::AutoText
4518 << 0 << 0 << 0
4519 << QString("<b>Hello</b>")
4520 << QString("Hello") + standard.at(i: 0)
4521 << 5 << 5 << 5
4522 << false << true;
4523
4524 QTest::newRow(dataTag: "before start")
4525 << standard.at(i: 0) << QQuickTextEdit::PlainText
4526 << 0 << 0 << -3
4527 << QString("Hello")
4528 << standard.at(i: 0)
4529 << 0 << 0 << 0
4530 << false << false;
4531
4532 QTest::newRow(dataTag: "past end")
4533 << standard.at(i: 0) << QQuickTextEdit::PlainText
4534 << 0 << 0 << standard.at(i: 0).length() + 3
4535 << QString("Hello")
4536 << standard.at(i: 0)
4537 << 0 << 0 << 0
4538 << false << false;
4539}
4540
4541void tst_qquicktextedit::insert()
4542{
4543 QFETCH(QString, text);
4544 QFETCH(QQuickTextEdit::TextFormat, textFormat);
4545 QFETCH(int, selectionStart);
4546 QFETCH(int, selectionEnd);
4547 QFETCH(int, insertPosition);
4548 QFETCH(QString, insertText);
4549 QFETCH(QString, expectedText);
4550 QFETCH(int, expectedSelectionStart);
4551 QFETCH(int, expectedSelectionEnd);
4552 QFETCH(int, expectedCursorPosition);
4553 QFETCH(bool, selectionChanged);
4554 QFETCH(bool, cursorPositionChanged);
4555
4556 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
4557 QQmlComponent textEditComponent(&engine);
4558 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4559 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4560 QVERIFY(textEdit != nullptr);
4561
4562 textEdit->setTextFormat(textFormat);
4563 textEdit->select(start: selectionStart, end: selectionEnd);
4564
4565 QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
4566 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
4567 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
4568 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
4569 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
4570
4571 textEdit->insert(position: insertPosition, text: insertText);
4572
4573 if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
4574 Qt::mightBeRichText(text) || Qt::mightBeRichText(insertText)))) {
4575 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
4576 } else {
4577 QCOMPARE(textEdit->text(), expectedText);
4578
4579 }
4580 QCOMPARE(textEdit->length(), expectedText.length());
4581
4582 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
4583 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
4584 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
4585
4586 if (selectionStart > selectionEnd)
4587 qSwap(value1&: selectionStart, value2&: selectionEnd);
4588
4589 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4590 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4591 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4592 QCOMPARE(textSpy.count() > 0, text != expectedText);
4593 QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
4594}
4595
4596void tst_qquicktextedit::remove_data()
4597{
4598 QTest::addColumn<QString>(name: "text");
4599 QTest::addColumn<QQuickTextEdit::TextFormat>(name: "textFormat");
4600 QTest::addColumn<int>(name: "selectionStart");
4601 QTest::addColumn<int>(name: "selectionEnd");
4602 QTest::addColumn<int>(name: "removeStart");
4603 QTest::addColumn<int>(name: "removeEnd");
4604 QTest::addColumn<QString>(name: "expectedText");
4605 QTest::addColumn<int>(name: "expectedSelectionStart");
4606 QTest::addColumn<int>(name: "expectedSelectionEnd");
4607 QTest::addColumn<int>(name: "expectedCursorPosition");
4608 QTest::addColumn<bool>(name: "selectionChanged");
4609 QTest::addColumn<bool>(name: "cursorPositionChanged");
4610
4611 const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
4612 const QString plainBoldText = QStringLiteral("This is some bold text");
4613
4614 QTest::newRow(dataTag: "from cursor position (beginning)")
4615 << standard.at(i: 0) << QQuickTextEdit::PlainText
4616 << 0 << 0
4617 << 0 << 5
4618 << standard.at(i: 0).mid(position: 5)
4619 << 0 << 0 << 0
4620 << false << false;
4621
4622 QTest::newRow(dataTag: "to cursor position (beginning)")
4623 << standard.at(i: 0) << QQuickTextEdit::PlainText
4624 << 0 << 0
4625 << 5 << 0
4626 << standard.at(i: 0).mid(position: 5)
4627 << 0 << 0 << 0
4628 << false << false;
4629
4630 QTest::newRow(dataTag: "to cursor position (end)")
4631 << standard.at(i: 0) << QQuickTextEdit::PlainText
4632 << standard.at(i: 0).length() << standard.at(i: 0).length()
4633 << standard.at(i: 0).length() << standard.at(i: 0).length() - 5
4634 << standard.at(i: 0).mid(position: 0, n: standard.at(i: 0).length() - 5)
4635 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5
4636 << false << true;
4637
4638 QTest::newRow(dataTag: "to cursor position (end)")
4639 << standard.at(i: 0) << QQuickTextEdit::PlainText
4640 << standard.at(i: 0).length() << standard.at(i: 0).length()
4641 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length()
4642 << standard.at(i: 0).mid(position: 0, n: standard.at(i: 0).length() - 5)
4643 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5
4644 << false << true;
4645
4646 QTest::newRow(dataTag: "from cursor position (middle)")
4647 << standard.at(i: 0) << QQuickTextEdit::PlainText
4648 << 18 << 18
4649 << 18 << 23
4650 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4651 << 18 << 18 << 18
4652 << false << false;
4653
4654 QTest::newRow(dataTag: "to cursor position (middle)")
4655 << standard.at(i: 0) << QQuickTextEdit::PlainText
4656 << 23 << 23
4657 << 18 << 23
4658 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4659 << 18 << 18 << 18
4660 << false << true;
4661
4662 QTest::newRow(dataTag: "after cursor position (beginning)")
4663 << standard.at(i: 0) << QQuickTextEdit::PlainText
4664 << 0 << 0
4665 << 18 << 23
4666 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4667 << 0 << 0 << 0
4668 << false << false;
4669
4670 QTest::newRow(dataTag: "before cursor position (end)")
4671 << standard.at(i: 0) << QQuickTextEdit::PlainText
4672 << standard.at(i: 0).length() << standard.at(i: 0).length()
4673 << 18 << 23
4674 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4675 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length() - 5
4676 << false << true;
4677
4678 QTest::newRow(dataTag: "before cursor position (middle)")
4679 << standard.at(i: 0) << QQuickTextEdit::PlainText
4680 << 23 << 23
4681 << 0 << 5
4682 << standard.at(i: 0).mid(position: 5)
4683 << 18 << 18 << 18
4684 << false << true;
4685
4686 QTest::newRow(dataTag: "after cursor position (middle)")
4687 << standard.at(i: 0) << QQuickTextEdit::PlainText
4688 << 18 << 18
4689 << 18 << 23
4690 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4691 << 18 << 18 << 18
4692 << false << false;
4693
4694 QTest::newRow(dataTag: "before selection")
4695 << standard.at(i: 0) << QQuickTextEdit::PlainText
4696 << 14 << 19
4697 << 0 << 5
4698 << standard.at(i: 0).mid(position: 5)
4699 << 9 << 14 << 14
4700 << true << true;
4701
4702 QTest::newRow(dataTag: "before reversed selection")
4703 << standard.at(i: 0) << QQuickTextEdit::PlainText
4704 << 19 << 14
4705 << 0 << 5
4706 << standard.at(i: 0).mid(position: 5)
4707 << 9 << 14 << 9
4708 << true << true;
4709
4710 QTest::newRow(dataTag: "after selection")
4711 << standard.at(i: 0) << QQuickTextEdit::PlainText
4712 << 14 << 19
4713 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length()
4714 << standard.at(i: 0).mid(position: 0, n: standard.at(i: 0).length() - 5)
4715 << 14 << 19 << 19
4716 << false << false;
4717
4718 QTest::newRow(dataTag: "after reversed selection")
4719 << standard.at(i: 0) << QQuickTextEdit::PlainText
4720 << 19 << 14
4721 << standard.at(i: 0).length() - 5 << standard.at(i: 0).length()
4722 << standard.at(i: 0).mid(position: 0, n: standard.at(i: 0).length() - 5)
4723 << 14 << 19 << 14
4724 << false << false;
4725
4726 QTest::newRow(dataTag: "from selection")
4727 << standard.at(i: 0) << QQuickTextEdit::PlainText
4728 << 14 << 24
4729 << 18 << 23
4730 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4731 << 14 << 19 << 19
4732 << true << true;
4733
4734 QTest::newRow(dataTag: "from reversed selection")
4735 << standard.at(i: 0) << QQuickTextEdit::PlainText
4736 << 24 << 14
4737 << 18 << 23
4738 << standard.at(i: 0).mid(position: 0, n: 18) + standard.at(i: 0).mid(position: 23)
4739 << 14 << 19 << 14
4740 << true << false;
4741
4742 QTest::newRow(dataTag: "plain text cropped beginning")
4743 << standard.at(i: 0) << QQuickTextEdit::PlainText
4744 << 0 << 0
4745 << -3 << 4
4746 << standard.at(i: 0).mid(position: 4)
4747 << 0 << 0 << 0
4748 << false << false;
4749
4750 QTest::newRow(dataTag: "plain text cropped end")
4751 << standard.at(i: 0) << QQuickTextEdit::PlainText
4752 << 0 << 0
4753 << 23 << standard.at(i: 0).length() + 8
4754 << standard.at(i: 0).mid(position: 0, n: 23)
4755 << 0 << 0 << 0
4756 << false << false;
4757
4758 QTest::newRow(dataTag: "plain text cropped beginning and end")
4759 << standard.at(i: 0) << QQuickTextEdit::PlainText
4760 << 0 << 0
4761 << -9 << standard.at(i: 0).length() + 4
4762 << QString()
4763 << 0 << 0 << 0
4764 << false << false;
4765
4766 QTest::newRow(dataTag: "all rich text")
4767 << richBoldText << QQuickTextEdit::RichText
4768 << 0 << 0
4769 << 0 << plainBoldText.length()
4770 << QString()
4771 << 0 << 0 << 0
4772 << false << false;
4773
4774 QTest::newRow(dataTag: "rick text sub string")
4775 << richBoldText << QQuickTextEdit::RichText
4776 << 0 << 0
4777 << 14 << 21
4778 << plainBoldText.mid(position: 0, n: 14) + plainBoldText.mid(position: 21)
4779 << 0 << 0 << 0
4780 << false << false;
4781}
4782
4783void tst_qquicktextedit::remove()
4784{
4785 QFETCH(QString, text);
4786 QFETCH(QQuickTextEdit::TextFormat, textFormat);
4787 QFETCH(int, selectionStart);
4788 QFETCH(int, selectionEnd);
4789 QFETCH(int, removeStart);
4790 QFETCH(int, removeEnd);
4791 QFETCH(QString, expectedText);
4792 QFETCH(int, expectedSelectionStart);
4793 QFETCH(int, expectedSelectionEnd);
4794 QFETCH(int, expectedCursorPosition);
4795 QFETCH(bool, selectionChanged);
4796 QFETCH(bool, cursorPositionChanged);
4797
4798 QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
4799 QQmlComponent textEditComponent(&engine);
4800 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4801 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4802 QVERIFY(textEdit != nullptr);
4803
4804 textEdit->setTextFormat(textFormat);
4805 textEdit->select(start: selectionStart, end: selectionEnd);
4806
4807 QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
4808 QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
4809 QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
4810 QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
4811 QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
4812
4813 textEdit->remove(start: removeStart, end: removeEnd);
4814
4815 if (textFormat == QQuickTextEdit::RichText
4816 || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
4817 QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
4818 } else {
4819 QCOMPARE(textEdit->text(), expectedText);
4820 }
4821 QCOMPARE(textEdit->length(), expectedText.length());
4822
4823 if (selectionStart > selectionEnd) //
4824 qSwap(value1&: selectionStart, value2&: selectionEnd);
4825
4826 QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
4827 QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
4828 QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
4829
4830 QCOMPARE(selectionSpy.count() > 0, selectionChanged);
4831 QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
4832 QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
4833 QCOMPARE(textSpy.count() > 0, text != expectedText);
4834
4835
4836 if (cursorPositionChanged) //
4837 QVERIFY(cursorPositionSpy.count() > 0);
4838}
4839
4840#if QT_CONFIG(shortcut)
4841
4842void tst_qquicktextedit::keySequence_data()
4843{
4844 QTest::addColumn<QString>(name: "text");
4845 QTest::addColumn<QKeySequence>(name: "sequence");
4846 QTest::addColumn<int>(name: "selectionStart");
4847 QTest::addColumn<int>(name: "selectionEnd");
4848 QTest::addColumn<int>(name: "cursorPosition");
4849 QTest::addColumn<QString>(name: "expectedText");
4850 QTest::addColumn<QString>(name: "selectedText");
4851 QTest::addColumn<Qt::Key>(name: "layoutDirection");
4852
4853 // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
4854
4855 QTest::newRow(dataTag: "select all")
4856 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
4857 << 44 << standard.at(i: 0) << standard.at(i: 0)
4858 << Qt::Key_Direction_L;
4859 QTest::newRow(dataTag: "select start of line")
4860 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
4861 << 0 << standard.at(i: 0) << standard.at(i: 0).mid(position: 0, n: 5)
4862 << Qt::Key_Direction_L;
4863 QTest::newRow(dataTag: "select start of block")
4864 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
4865 << 0 << standard.at(i: 0) << standard.at(i: 0).mid(position: 0, n: 5)
4866 << Qt::Key_Direction_L;
4867 QTest::newRow(dataTag: "select end of line")
4868 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
4869 << 44 << standard.at(i: 0) << standard.at(i: 0).mid(position: 5)
4870 << Qt::Key_Direction_L;
4871 QTest::newRow(dataTag: "select end of document")
4872 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
4873 << 44 << standard.at(i: 0) << standard.at(i: 0).mid(position: 3)
4874 << Qt::Key_Direction_L;
4875 QTest::newRow(dataTag: "select end of block")
4876 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
4877 << 44 << standard.at(i: 0) << standard.at(i: 0).mid(position: 18)
4878 << Qt::Key_Direction_L;
4879 QTest::newRow(dataTag: "delete end of line")
4880 << standard.at(i: 0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
4881 << 24 << standard.at(i: 0).mid(position: 0, n: 24) << QString()
4882 << Qt::Key_Direction_L;
4883 QTest::newRow(dataTag: "move to start of line")
4884 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
4885 << 0 << standard.at(i: 0) << QString()
4886 << Qt::Key_Direction_L;
4887 QTest::newRow(dataTag: "move to start of block")
4888 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
4889 << 0 << standard.at(i: 0) << QString()
4890 << Qt::Key_Direction_L;
4891 QTest::newRow(dataTag: "move to next char")
4892 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
4893 << 13 << standard.at(i: 0) << QString()
4894 << Qt::Key_Direction_L;
4895 QTest::newRow(dataTag: "move to previous char (ltr)")
4896 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4897 << 2 << standard.at(i: 0) << QString()
4898 << Qt::Key_Direction_L;
4899 QTest::newRow(dataTag: "move to previous char (rtl)")
4900 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
4901 << 4 << standard.at(i: 0) << QString()
4902 << Qt::Key_Direction_R;
4903 QTest::newRow(dataTag: "move to previous char with selection")
4904 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4905 << 3 << standard.at(i: 0) << QString()
4906 << Qt::Key_Direction_L;
4907 QTest::newRow(dataTag: "select next char (ltr)")
4908 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4909 << 24 << standard.at(i: 0) << standard.at(i: 0).mid(position: 23, n: 1)
4910 << Qt::Key_Direction_L;
4911 QTest::newRow(dataTag: "select next char (rtl)")
4912 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4913 << 22 << standard.at(i: 0) << standard.at(i: 0).mid(position: 22, n: 1)
4914 << Qt::Key_Direction_R;
4915 QTest::newRow(dataTag: "select previous char (ltr)")
4916 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4917 << 18 << standard.at(i: 0) << standard.at(i: 0).mid(position: 18, n: 1)
4918 << Qt::Key_Direction_L;
4919 QTest::newRow(dataTag: "select previous char (rtl)")
4920 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4921 << 20 << standard.at(i: 0) << standard.at(i: 0).mid(position: 19, n: 1)
4922 << Qt::Key_Direction_R;
4923 QTest::newRow(dataTag: "move to next word (ltr)")
4924 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4925 << 10 << standard.at(i: 0) << QString()
4926 << Qt::Key_Direction_L;
4927 QTest::newRow(dataTag: "move to next word (rtl)")
4928 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4929 << 4 << standard.at(i: 0) << QString()
4930 << Qt::Key_Direction_R;
4931 QTest::newRow(dataTag: "move to previous word (ltr)")
4932 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4933 << 4 << standard.at(i: 0) << QString()
4934 << Qt::Key_Direction_L;
4935 QTest::newRow(dataTag: "move to previous word (rlt)")
4936 << standard.at(i: 0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4937 << 10 << standard.at(i: 0) << QString()
4938 << Qt::Key_Direction_R;
4939 QTest::newRow(dataTag: "select next word")
4940 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4941 << 16 << standard.at(i: 0) << standard.at(i: 0).mid(position: 11, n: 5)
4942 << Qt::Key_Direction_L;
4943 QTest::newRow(dataTag: "select previous word")
4944 << standard.at(i: 0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4945 << 10 << standard.at(i: 0) << standard.at(i: 0).mid(position: 10, n: 1)
4946 << Qt::Key_Direction_L;
4947 QTest::newRow(dataTag: "delete (selection)")
4948 << standard.at(i: 0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4949 << 12 << (standard.at(i: 0).mid(position: 0, n: 12) + standard.at(i: 0).mid(position: 15)) << QString()
4950 << Qt::Key_Direction_L;
4951 QTest::newRow(dataTag: "delete (no selection)")
4952 << standard.at(i: 0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4953 << 15 << (standard.at(i: 0).mid(position: 0, n: 15) + standard.at(i: 0).mid(position: 16)) << QString()
4954 << Qt::Key_Direction_L;
4955 QTest::newRow(dataTag: "delete end of word")
4956 << standard.at(i: 0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4957 << 24 << (standard.at(i: 0).mid(position: 0, n: 24) + standard.at(i: 0).mid(position: 27)) << QString()
4958 << Qt::Key_Direction_L;
4959 QTest::newRow(dataTag: "delete start of word")
4960 << standard.at(i: 0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4961 << 4 << (standard.at(i: 0).mid(position: 0, n: 4) + standard.at(i: 0).mid(position: 7)) << QString()
4962 << Qt::Key_Direction_L;
4963}
4964
4965void tst_qquicktextedit::keySequence()
4966{
4967 QFETCH(QString, text);
4968 QFETCH(QKeySequence, sequence);
4969 QFETCH(int, selectionStart);
4970 QFETCH(int, selectionEnd);
4971 QFETCH(int, cursorPosition);
4972 QFETCH(QString, expectedText);
4973 QFETCH(QString, selectedText);
4974 QFETCH(Qt::Key, layoutDirection);
4975
4976 if (sequence.isEmpty()) {
4977 QSKIP("Key sequence is undefined");
4978 }
4979
4980 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; text: \"" + text + "\" }";
4981 QQmlComponent textEditComponent(&engine);
4982 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
4983 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
4984 QVERIFY(textEdit != nullptr);
4985
4986 QQuickWindow window;
4987 textEdit->setParentItem(window.contentItem());
4988 window.show();
4989 window.requestActivate();
4990 QVERIFY(QTest::qWaitForWindowActive(&window));
4991
4992 QVERIFY(textEdit->hasActiveFocus());
4993
4994 simulateKey(view: &window, key: layoutDirection);
4995
4996 textEdit->select(start: selectionStart, end: selectionEnd);
4997
4998 simulateKeys(window: &window, sequence);
4999
5000 QCOMPARE(textEdit->cursorPosition(), cursorPosition);
5001 QCOMPARE(textEdit->text(), expectedText);
5002 QCOMPARE(textEdit->selectedText(), selectedText);
5003}
5004
5005#endif // QT_CONFIG(shortcut)
5006
5007#define NORMAL 0
5008#define REPLACE_UNTIL_END 1
5009
5010void tst_qquicktextedit::undo_data()
5011{
5012 QTest::addColumn<QStringList>(name: "insertString");
5013 QTest::addColumn<IntList>(name: "insertIndex");
5014 QTest::addColumn<IntList>(name: "insertMode");
5015 QTest::addColumn<QStringList>(name: "expectedString");
5016 QTest::addColumn<bool>(name: "use_keys");
5017
5018 for (int i=0; i<2; i++) {
5019 QString keys_str = "keyboard";
5020 bool use_keys = true;
5021 if (i==0) {
5022 keys_str = "insert";
5023 use_keys = false;
5024 }
5025
5026 {
5027 IntList insertIndex;
5028 IntList insertMode;
5029 QStringList insertString;
5030 QStringList expectedString;
5031
5032 insertIndex << -1;
5033 insertMode << NORMAL;
5034 insertString << "1";
5035
5036 insertIndex << -1;
5037 insertMode << NORMAL;
5038 insertString << "5";
5039
5040 insertIndex << 1;
5041 insertMode << NORMAL;
5042 insertString << "3";
5043
5044 insertIndex << 1;
5045 insertMode << NORMAL;
5046 insertString << "2";
5047
5048 insertIndex << 3;
5049 insertMode << NORMAL;
5050 insertString << "4";
5051
5052 expectedString << "12345";
5053 expectedString << "1235";
5054 expectedString << "135";
5055 expectedString << "15";
5056 expectedString << "";
5057
5058 QTest::newRow(dataTag: QString(keys_str + "_numbers").toLatin1()) <<
5059 insertString <<
5060 insertIndex <<
5061 insertMode <<
5062 expectedString <<
5063 bool(use_keys);
5064 }
5065 {
5066 IntList insertIndex;
5067 IntList insertMode;
5068 QStringList insertString;
5069 QStringList expectedString;
5070
5071 insertIndex << -1;
5072 insertMode << NORMAL;
5073 insertString << "World"; // World
5074
5075 insertIndex << 0;
5076 insertMode << NORMAL;
5077 insertString << "Hello"; // HelloWorld
5078
5079 insertIndex << 0;
5080 insertMode << NORMAL;
5081 insertString << "Well"; // WellHelloWorld
5082
5083 insertIndex << 9;
5084 insertMode << NORMAL;
5085 insertString << "There"; // WellHelloThereWorld;
5086
5087 expectedString << "WellHelloThereWorld";
5088 expectedString << "WellHelloWorld";
5089 expectedString << "HelloWorld";
5090 expectedString << "World";
5091 expectedString << "";
5092
5093 QTest::newRow(dataTag: QString(keys_str + "_helloworld").toLatin1()) <<
5094 insertString <<
5095 insertIndex <<
5096 insertMode <<
5097 expectedString <<
5098 bool(use_keys);
5099 }
5100 {
5101 IntList insertIndex;
5102 IntList insertMode;
5103 QStringList insertString;
5104 QStringList expectedString;
5105
5106 insertIndex << -1;
5107 insertMode << NORMAL;
5108 insertString << "Ensuring";
5109
5110 insertIndex << -1;
5111 insertMode << NORMAL;
5112 insertString << " instan";
5113
5114 insertIndex << 9;
5115 insertMode << NORMAL;
5116 insertString << "an ";
5117
5118 insertIndex << 10;
5119 insertMode << REPLACE_UNTIL_END;
5120 insertString << " unique instance.";
5121
5122 expectedString << "Ensuring a unique instance.";
5123 expectedString << "Ensuring an instan";
5124 expectedString << "Ensuring instan";
5125 expectedString << "";
5126
5127 QTest::newRow(dataTag: QString(keys_str + "_patterns").toLatin1()) <<
5128 insertString <<
5129 insertIndex <<
5130 insertMode <<
5131 expectedString <<
5132 bool(use_keys);
5133 }
5134 }
5135}
5136
5137void tst_qquicktextedit::undo()
5138{
5139 QFETCH(QStringList, insertString);
5140 QFETCH(IntList, insertIndex);
5141 QFETCH(IntList, insertMode);
5142 QFETCH(QStringList, expectedString);
5143
5144 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
5145 QQmlComponent textEditComponent(&engine);
5146 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
5147 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
5148 QVERIFY(textEdit != nullptr);
5149
5150 QQuickWindow window;
5151 textEdit->setParentItem(window.contentItem());
5152 window.show();
5153 window.requestActivate();
5154 QVERIFY(QTest::qWaitForWindowActive(&window));
5155
5156 QVERIFY(textEdit->hasActiveFocus());
5157 QVERIFY(!textEdit->canUndo());
5158
5159 QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
5160
5161 int i;
5162
5163// STEP 1: First build up an undo history by inserting or typing some strings...
5164 for (i = 0; i < insertString.size(); ++i) {
5165 if (insertIndex[i] > -1)
5166 textEdit->setCursorPosition(insertIndex[i]);
5167
5168 // experimental stuff
5169 if (insertMode[i] == REPLACE_UNTIL_END) {
5170 textEdit->select(start: insertIndex[i], end: insertIndex[i] + 8);
5171
5172 // This is what I actually want...
5173 // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
5174 }
5175
5176 for (int j = 0; j < insertString.at(i).length(); j++)
5177 QTest::keyClick(window: &window, key: insertString.at(i).at(i: j).toLatin1());
5178 }
5179
5180 QCOMPARE(spy.count(), 1);
5181
5182// STEP 2: Next call undo several times and see if we can restore to the previous state
5183 for (i = 0; i < expectedString.size() - 1; ++i) {
5184 QCOMPARE(textEdit->text(), expectedString[i]);
5185 QVERIFY(textEdit->canUndo());
5186 textEdit->undo();
5187 }
5188
5189// STEP 3: Verify that we have undone everything
5190 QVERIFY(textEdit->text().isEmpty());
5191 QVERIFY(!textEdit->canUndo());
5192 QCOMPARE(spy.count(), 2);
5193}
5194
5195void tst_qquicktextedit::redo_data()
5196{
5197 QTest::addColumn<QStringList>(name: "insertString");
5198 QTest::addColumn<IntList>(name: "insertIndex");
5199 QTest::addColumn<QStringList>(name: "expectedString");
5200
5201 {
5202 IntList insertIndex;
5203 QStringList insertString;
5204 QStringList expectedString;
5205
5206 insertIndex << -1;
5207 insertString << "World"; // World
5208 insertIndex << 0;
5209 insertString << "Hello"; // HelloWorld
5210 insertIndex << 0;
5211 insertString << "Well"; // WellHelloWorld
5212 insertIndex << 9;
5213 insertString << "There"; // WellHelloThereWorld;
5214
5215 expectedString << "World";
5216 expectedString << "HelloWorld";
5217 expectedString << "WellHelloWorld";
5218 expectedString << "WellHelloThereWorld";
5219
5220 QTest::newRow(dataTag: "Inserts and setting cursor") << insertString << insertIndex << expectedString;
5221 }
5222}
5223
5224void tst_qquicktextedit::redo()
5225{
5226 QFETCH(QStringList, insertString);
5227 QFETCH(IntList, insertIndex);
5228 QFETCH(QStringList, expectedString);
5229
5230 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
5231 QQmlComponent textEditComponent(&engine);
5232 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
5233 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
5234 QVERIFY(textEdit != nullptr);
5235
5236 QQuickWindow window;
5237 textEdit->setParentItem(window.contentItem());
5238 window.show();
5239 window.requestActivate();
5240 QVERIFY(QTest::qWaitForWindowActive(&window));
5241 QVERIFY(textEdit->hasActiveFocus());
5242
5243 QVERIFY(!textEdit->canUndo());
5244 QVERIFY(!textEdit->canRedo());
5245
5246 QSignalSpy spy(textEdit, SIGNAL(canRedoChanged()));
5247
5248 int i;
5249 // inserts the diff strings at diff positions
5250 for (i = 0; i < insertString.size(); ++i) {
5251 if (insertIndex[i] > -1)
5252 textEdit->setCursorPosition(insertIndex[i]);
5253 for (int j = 0; j < insertString.at(i).length(); j++)
5254 QTest::keyClick(window: &window, key: insertString.at(i).at(i: j).toLatin1());
5255 QVERIFY(textEdit->canUndo());
5256 QVERIFY(!textEdit->canRedo());
5257 }
5258
5259 QCOMPARE(spy.count(), 0);
5260
5261 // undo everything
5262 while (!textEdit->text().isEmpty()) {
5263 QVERIFY(textEdit->canUndo());
5264 textEdit->undo();
5265 QVERIFY(textEdit->canRedo());
5266 }
5267
5268 QCOMPARE(spy.count(), 1);
5269
5270 for (i = 0; i < expectedString.size(); ++i) {
5271 QVERIFY(textEdit->canRedo());
5272 textEdit->redo();
5273 QCOMPARE(textEdit->text() , expectedString[i]);
5274 QVERIFY(textEdit->canUndo());
5275 }
5276 QVERIFY(!textEdit->canRedo());
5277 QCOMPARE(spy.count(), 2);
5278}
5279
5280#if QT_CONFIG(shortcut)
5281
5282void tst_qquicktextedit::undo_keypressevents_data()
5283{
5284 QTest::addColumn<KeyList>(name: "keys");
5285 QTest::addColumn<QStringList>(name: "expectedString");
5286
5287 {
5288 KeyList keys;
5289 QStringList expectedString;
5290
5291 keys << "AFRAID"
5292 << Qt::Key_Home
5293 << "VERY"
5294 << Qt::Key_Left
5295 << Qt::Key_Left
5296 << Qt::Key_Left
5297 << Qt::Key_Left
5298 << "BE"
5299 << Qt::Key_End
5300 << "!";
5301
5302 expectedString << "BEVERYAFRAID!";
5303 expectedString << "BEVERYAFRAID";
5304 expectedString << "VERYAFRAID";
5305 expectedString << "AFRAID";
5306
5307 QTest::newRow(dataTag: "Inserts and moving cursor") << keys << expectedString;
5308 } {
5309 KeyList keys;
5310 QStringList expectedString;
5311
5312 // inserting '1234'
5313 keys << "1234" << Qt::Key_Home
5314 // skipping '12'
5315 << Qt::Key_Right << Qt::Key_Right
5316 // selecting '34'
5317 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5318 // deleting '34'
5319 << Qt::Key_Delete;
5320
5321 expectedString << "12";
5322 expectedString << "1234";
5323
5324 QTest::newRow(dataTag: "Inserts,moving,selection and delete") << keys << expectedString;
5325 } {
5326 KeyList keys;
5327 QStringList expectedString;
5328
5329 // inserting 'AB12'
5330 keys << "AB12"
5331 << Qt::Key_Home
5332 // selecting 'AB'
5333 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5334 << Qt::Key_Delete
5335 << QKeySequence::Undo
5336 // ### Text is selected in text input
5337// << Qt::Key_Right
5338 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
5339 << Qt::Key_Delete;
5340
5341 expectedString << "AB";
5342 expectedString << "AB12";
5343
5344 QTest::newRow(dataTag: "Inserts,moving,selection, delete and undo") << keys << expectedString;
5345 } {
5346 KeyList keys;
5347 QStringList expectedString;
5348
5349 // inserting 'ABCD'
5350 keys << "abcd"
5351 //move left two
5352 << Qt::Key_Left << Qt::Key_Left
5353 // inserting '1234'
5354 << "1234"
5355 // selecting '1234'
5356 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5357 // overwriting '1234' with '5'
5358 << "5"
5359 // undoing deletion of 'AB'
5360 << QKeySequence::Undo
5361 // ### Text is selected in text input
5362 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
5363 // overwriting '1234' with '6'
5364 << "6";
5365
5366 expectedString << "ab6cd";
5367 // for versions previous to 3.2 we overwrite needed two undo operations
5368 expectedString << "ab1234cd";
5369 expectedString << "abcd";
5370
5371 QTest::newRow(dataTag: "Inserts,moving,selection and undo, removing selection") << keys << expectedString;
5372 } {
5373 KeyList keys;
5374 QStringList expectedString;
5375
5376 // inserting 'ABC'
5377 keys << "ABC"
5378 // removes 'C'
5379 << Qt::Key_Backspace;
5380
5381 expectedString << "AB";
5382 expectedString << "ABC";
5383
5384 QTest::newRow(dataTag: "Inserts,backspace") << keys << expectedString;
5385 } {
5386 KeyList keys;
5387 QStringList expectedString;
5388
5389 keys << "ABC"
5390 // removes 'C'
5391 << Qt::Key_Backspace
5392 // inserting 'Z'
5393 << "Z";
5394
5395 expectedString << "ABZ";
5396 expectedString << "AB";
5397 expectedString << "ABC";
5398
5399 QTest::newRow(dataTag: "Inserts,backspace,inserts") << keys << expectedString;
5400 } {
5401 KeyList keys;
5402 QStringList expectedString;
5403
5404 // inserting '123'
5405 keys << "123" << Qt::Key_Home
5406 // selecting '123'
5407 << (Qt::Key_End | Qt::ShiftModifier)
5408 // overwriting '123' with 'ABC'
5409 << "ABC";
5410
5411 expectedString << "ABC";
5412 expectedString << "123";
5413
5414 QTest::newRow(dataTag: "Inserts,moving,selection and overwriting") << keys << expectedString;
5415 }
5416
5417 bool canCopyPaste = PlatformQuirks::isClipboardAvailable();
5418
5419 if (canCopyPaste) {
5420 KeyList keys;
5421 keys << "123"
5422 << QKeySequence(QKeySequence::SelectStartOfLine)
5423 << QKeySequence(QKeySequence::Cut)
5424 << "ABC"
5425 << QKeySequence(QKeySequence::Paste);
5426 QStringList expectedString = QStringList()
5427 << "ABC123"
5428 << "ABC"
5429 << ""
5430 << "123";
5431 QTest::newRow(dataTag: "Cut,paste") << keys << expectedString;
5432 }
5433 if (canCopyPaste) {
5434 KeyList keys;
5435 keys << "123"
5436 << QKeySequence(QKeySequence::SelectStartOfLine)
5437 << QKeySequence(QKeySequence::Copy)
5438 << "ABC"
5439 << QKeySequence(QKeySequence::Paste);
5440 QStringList expectedString = QStringList()
5441 << "ABC123"
5442 << "ABC"
5443 << "123";
5444 QTest::newRow(dataTag: "Copy,paste") << keys << expectedString;
5445 }
5446}
5447
5448void tst_qquicktextedit::undo_keypressevents()
5449{
5450 QFETCH(KeyList, keys);
5451 QFETCH(QStringList, expectedString);
5452
5453 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
5454 QQmlComponent textEditComponent(&engine);
5455 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
5456 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
5457 QVERIFY(textEdit != nullptr);
5458
5459 QQuickWindow window;
5460 textEdit->setParentItem(window.contentItem());
5461 window.show();
5462 window.requestActivate();
5463 QVERIFY(QTest::qWaitForWindowActive(&window));
5464 QVERIFY(textEdit->hasActiveFocus());
5465
5466 simulateKeys(window: &window, keys);
5467
5468 for (int i = 0; i < expectedString.size(); ++i) {
5469 QCOMPARE(textEdit->text() , expectedString[i]);
5470 textEdit->undo();
5471 }
5472 QVERIFY(textEdit->text().isEmpty());
5473}
5474
5475#endif // QT_CONFIG(shortcut)
5476
5477void tst_qquicktextedit::clear()
5478{
5479 QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
5480 QQmlComponent textEditComponent(&engine);
5481 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
5482 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
5483 QVERIFY(textEdit != nullptr);
5484
5485 QQuickWindow window;
5486 textEdit->setParentItem(window.contentItem());
5487 window.show();
5488 window.requestActivate();
5489 QVERIFY(QTest::qWaitForWindowActive(&window));
5490 QVERIFY(textEdit->hasActiveFocus());
5491
5492 QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
5493
5494 textEdit->setText("I am Legend");
5495 QCOMPARE(textEdit->text(), QString("I am Legend"));
5496 textEdit->clear();
5497 QVERIFY(textEdit->text().isEmpty());
5498
5499 QCOMPARE(spy.count(), 1);
5500
5501 // checks that clears can be undone
5502 textEdit->undo();
5503 QVERIFY(!textEdit->canUndo());
5504 QCOMPARE(spy.count(), 2);
5505 QCOMPARE(textEdit->text(), QString("I am Legend"));
5506
5507 textEdit->setCursorPosition(4);
5508 QInputMethodEvent preeditEvent("PREEDIT", QList<QInputMethodEvent::Attribute>());
5509 QGuiApplication::sendEvent(receiver: textEdit, event: &preeditEvent);
5510 QCOMPARE(textEdit->text(), QString("I am Legend"));
5511 QCOMPARE(textEdit->preeditText(), QString("PREEDIT"));
5512
5513 textEdit->clear();
5514 QVERIFY(textEdit->text().isEmpty());
5515
5516 QCOMPARE(spy.count(), 3);
5517
5518 // checks that clears can be undone
5519 textEdit->undo();
5520 QVERIFY(!textEdit->canUndo());
5521 QCOMPARE(spy.count(), 4);
5522 QCOMPARE(textEdit->text(), QString("I am Legend"));
5523
5524 textEdit->setText(QString("<i>I am Legend</i>"));
5525 QCOMPARE(textEdit->text(), QString("<i>I am Legend</i>"));
5526 textEdit->clear();
5527 QVERIFY(textEdit->text().isEmpty());
5528
5529 QCOMPARE(spy.count(), 5);
5530
5531 // checks that clears can be undone
5532 textEdit->undo();
5533 QCOMPARE(spy.count(), 6);
5534 QCOMPARE(textEdit->text(), QString("<i>I am Legend</i>"));
5535}
5536
5537void tst_qquicktextedit::baseUrl()
5538{
5539 QUrl localUrl("file:///tests/text.qml");
5540 QUrl remoteUrl("http://www.qt-project.org/test.qml");
5541
5542 QQmlComponent textComponent(&engine);
5543 textComponent.setData("import QtQuick 2.0\n TextEdit {}", baseUrl: localUrl);
5544 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object: textComponent.create());
5545
5546 QCOMPARE(textObject->baseUrl(), localUrl);
5547
5548 QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
5549
5550 textObject->setBaseUrl(localUrl);
5551 QCOMPARE(textObject->baseUrl(), localUrl);
5552 QCOMPARE(spy.count(), 0);
5553
5554 textObject->setBaseUrl(remoteUrl);
5555 QCOMPARE(textObject->baseUrl(), remoteUrl);
5556 QCOMPARE(spy.count(), 1);
5557
5558 textObject->resetBaseUrl();
5559 QCOMPARE(textObject->baseUrl(), localUrl);
5560 QCOMPARE(spy.count(), 2);
5561}
5562
5563void tst_qquicktextedit::embeddedImages_data()
5564{
5565 QTest::addColumn<QUrl>(name: "qmlfile");
5566 QTest::addColumn<QString>(name: "error");
5567 QTest::newRow(dataTag: "local") << testFileUrl(fileName: "embeddedImagesLocal.qml") << "";
5568 QTest::newRow(dataTag: "local-error") << testFileUrl(fileName: "embeddedImagesLocalError.qml")
5569 << testFileUrl(fileName: "embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl(fileName: "http/notexists.png").toString();
5570 QTest::newRow(dataTag: "local") << testFileUrl(fileName: "embeddedImagesLocalRelative.qml") << "";
5571 QTest::newRow(dataTag: "remote") << testFileUrl(fileName: "embeddedImagesRemote.qml") << "";
5572 QTest::newRow(dataTag: "remote-error") << testFileUrl(fileName: "embeddedImagesRemoteError.qml")
5573 << testFileUrl(fileName: "embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error transferring {{ServerBaseUrl}}/notexists.png - server replied: Not found";
5574 QTest::newRow(dataTag: "remote") << testFileUrl(fileName: "embeddedImagesRemoteRelative.qml") << "";
5575}
5576
5577void tst_qquicktextedit::embeddedImages()
5578{
5579 QFETCH(QUrl, qmlfile);
5580 QFETCH(QString, error);
5581
5582 TestHTTPServer server;
5583 QVERIFY2(server.listen(), qPrintable(server.errorString()));
5584 server.serveDirectory(testFile(fileName: "http"));
5585
5586 error.replace(QStringLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString());
5587
5588 if (!error.isEmpty())
5589 QTest::ignoreMessage(type: QtWarningMsg, message: error.toLatin1());
5590
5591 QQmlComponent textComponent(&engine, qmlfile);
5592 QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(object: textComponent.beginCreate(engine.rootContext()));
5593 QVERIFY(textObject != nullptr);
5594
5595 const int baseUrlPropertyIndex = textObject->metaObject()->indexOfProperty(name: "serverBaseUrl");
5596 if (baseUrlPropertyIndex != -1) {
5597 QMetaProperty prop = textObject->metaObject()->property(index: baseUrlPropertyIndex);
5598 QVERIFY(prop.write(textObject, server.baseUrl().toString()));
5599 }
5600
5601 textComponent.completeCreate();
5602
5603 QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
5604
5605 QPixmap pm(testFile(fileName: "http/exists.png"));
5606 if (error.isEmpty()) {
5607 QCOMPARE(textObject->width(), double(pm.width()));
5608 QCOMPARE(textObject->height(), double(pm.height()));
5609 } else {
5610 QVERIFY(16 != pm.width()); // check test is effective
5611 QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
5612 QCOMPARE(textObject->height(), 16.0);
5613 }
5614
5615 delete textObject;
5616}
5617
5618void tst_qquicktextedit::emptytags_QTBUG_22058()
5619{
5620 QQuickView window(testFileUrl(fileName: "qtbug-22058.qml"));
5621 QVERIFY(window.rootObject() != nullptr);
5622
5623 window.show();
5624 window.requestActivate();
5625 QVERIFY(QTest::qWaitForWindowActive(&window));
5626 QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(object: qvariant_cast<QObject *>(v: window.rootObject()->property(name: "inputField")));
5627 QVERIFY(input->hasActiveFocus());
5628
5629 QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
5630 event.setCommitString(commitString: "<b>Bold<");
5631 QGuiApplication::sendEvent(receiver: input, event: &event);
5632 QCOMPARE(input->text(), QString("<b>Bold<"));
5633 event.setCommitString(commitString: ">");
5634 QGuiApplication::sendEvent(receiver: input, event: &event);
5635 QCOMPARE(input->text(), QString("<b>Bold<>"));
5636}
5637
5638void tst_qquicktextedit::cursorRectangle_QTBUG_38947()
5639{
5640 QQuickView window(testFileUrl(fileName: "qtbug-38947.qml"));
5641
5642 window.show();
5643 window.requestActivate();
5644 QVERIFY(QTest::qWaitForWindowExposed(&window));
5645 QQuickTextEdit *edit = window.rootObject()->findChild<QQuickTextEdit *>(aName: "textedit");
5646 QVERIFY(edit);
5647
5648 QPoint from = edit->positionToRectangle(0).center().toPoint();
5649 QTest::mousePress(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: from);
5650
5651 QSignalSpy spy(edit, SIGNAL(cursorRectangleChanged()));
5652 QVERIFY(spy.isValid());
5653
5654 for (int i = 1; i < edit->length() - 1; ++i) {
5655 QRectF rect = edit->positionToRectangle(i);
5656 QTest::mouseMove(window: &window, pos: rect.center().toPoint());
5657 QCOMPARE(edit->cursorRectangle(), rect);
5658 QCOMPARE(spy.count(), i);
5659 }
5660
5661 QPoint to = edit->positionToRectangle(edit->length() - 1).center().toPoint();
5662 QTest::mouseRelease(window: &window, button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: to);
5663}
5664
5665void tst_qquicktextedit::textCached_QTBUG_41583()
5666{
5667 QQmlComponent component(&engine);
5668 component.setData("import QtQuick 2.0\nTextEdit { property int margin: 10; text: \"TextEdit\"; textMargin: margin; property bool empty: !text; }", baseUrl: QUrl());
5669 QQuickTextEdit *textedit = qobject_cast<QQuickTextEdit*>(object: component.create());
5670 QVERIFY(textedit);
5671 QCOMPARE(textedit->textMargin(), qreal(10.0));
5672 QCOMPARE(textedit->text(), QString("TextEdit"));
5673 QVERIFY(!textedit->property("empty").toBool());
5674}
5675
5676void tst_qquicktextedit::doubleSelect_QTBUG_38704()
5677{
5678 QString componentStr = "import QtQuick 2.2\nTextEdit { text: \"TextEdit\" }";
5679 QQmlComponent textEditComponent(&engine);
5680 textEditComponent.setData(componentStr.toLatin1(), baseUrl: QUrl());
5681 QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(object: textEditComponent.create());
5682 QVERIFY(textEdit != nullptr);
5683
5684 QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
5685
5686 textEdit->select(start: 0,end: 1); //Select some text initially
5687 QCOMPARE(selectionSpy.count(), 1);
5688 textEdit->select(start: 0,end: 1); //No change to selection start/end
5689 QCOMPARE(selectionSpy.count(), 1);
5690 textEdit->select(start: 0,end: 2); //Change selection end
5691 QCOMPARE(selectionSpy.count(), 2);
5692 textEdit->select(start: 1,end: 2); //Change selection start
5693 QCOMPARE(selectionSpy.count(), 3);
5694}
5695
5696void tst_qquicktextedit::padding()
5697{
5698 QScopedPointer<QQuickView> window(new QQuickView);
5699 window->setSource(testFileUrl(fileName: "padding.qml"));
5700 QTRY_COMPARE(window->status(), QQuickView::Ready);
5701 window->show();
5702 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
5703 QQuickItem *root = window->rootObject();
5704 QVERIFY(root);
5705 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(object: root);
5706 QVERIFY(obj != nullptr);
5707
5708 qreal cw = obj->contentWidth();
5709 qreal ch = obj->contentHeight();
5710
5711 QVERIFY(cw > 0);
5712 QVERIFY(ch > 0);
5713
5714 QCOMPARE(obj->topPadding(), 20.0);
5715 QCOMPARE(obj->leftPadding(), 30.0);
5716 QCOMPARE(obj->rightPadding(), 40.0);
5717 QCOMPARE(obj->bottomPadding(), 50.0);
5718
5719 QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
5720 QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
5721
5722 obj->setTopPadding(2.25);
5723 QCOMPARE(obj->topPadding(), 2.25);
5724 QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
5725
5726 obj->setLeftPadding(3.75);
5727 QCOMPARE(obj->leftPadding(), 3.75);
5728 QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
5729
5730 obj->setRightPadding(4.4);
5731 QCOMPARE(obj->rightPadding(), 4.4);
5732 QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
5733
5734 obj->setBottomPadding(1.11);
5735 QCOMPARE(obj->bottomPadding(), 1.11);
5736 QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
5737
5738 obj->setText("Qt");
5739 QVERIFY(obj->contentWidth() < cw);
5740 QCOMPARE(obj->contentHeight(), ch);
5741 cw = obj->contentWidth();
5742
5743 QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
5744 QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
5745
5746 obj->setFont(QFont("Courier", 96));
5747 QVERIFY(obj->contentWidth() > cw);
5748 QVERIFY(obj->contentHeight() > ch);
5749 cw = obj->contentWidth();
5750 ch = obj->contentHeight();
5751
5752 QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
5753 QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
5754
5755 obj->resetTopPadding();
5756 QCOMPARE(obj->topPadding(), 10.0);
5757 obj->resetLeftPadding();
5758 QCOMPARE(obj->leftPadding(), 10.0);
5759 obj->resetRightPadding();
5760 QCOMPARE(obj->rightPadding(), 10.0);
5761 obj->resetBottomPadding();
5762 QCOMPARE(obj->bottomPadding(), 10.0);
5763
5764 obj->resetPadding();
5765 QCOMPARE(obj->padding(), 0.0);
5766 QCOMPARE(obj->topPadding(), 0.0);
5767 QCOMPARE(obj->leftPadding(), 0.0);
5768 QCOMPARE(obj->rightPadding(), 0.0);
5769 QCOMPARE(obj->bottomPadding(), 0.0);
5770
5771 delete root;
5772}
5773
5774void tst_qquicktextedit::paddingAndWrap()
5775{
5776 // Check that the document ends up with the correct width if
5777 // we set left and right padding after component completed.
5778 QScopedPointer<QQuickView> window(new QQuickView);
5779 window->setSource(testFileUrl(fileName: "wordwrap.qml"));
5780 QTRY_COMPARE(window->status(), QQuickView::Ready);
5781 window->show();
5782 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
5783 QQuickItem *root = window->rootObject();
5784 QVERIFY(root);
5785 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit *>(object: root);
5786 QVERIFY(obj != nullptr);
5787 QTextDocument *doc = QQuickTextEditPrivate::get(item: obj)->document;
5788
5789 QCOMPARE(doc->textWidth(), obj->width());
5790 obj->setLeftPadding(10);
5791 obj->setRightPadding(10);
5792 QCOMPARE(doc->textWidth(), obj->width() - obj->leftPadding() - obj->rightPadding());
5793 obj->setLeftPadding(0);
5794 obj->setRightPadding(0);
5795 QCOMPARE(doc->textWidth(), obj->width());
5796}
5797
5798void tst_qquicktextedit::QTBUG_51115_readOnlyResetsSelection()
5799{
5800 QQuickView view;
5801 view.setSource(testFileUrl(fileName: "qtbug51115.qml"));
5802 view.show();
5803 QVERIFY(QTest::qWaitForWindowExposed(&view));
5804 QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(object: view.rootObject());
5805
5806 QCOMPARE(obj->selectedText(), QString());
5807}
5808
5809void tst_qquicktextedit::keys_shortcutoverride()
5810{
5811 // Tests that QML TextEdit receives Keys.onShortcutOverride (QTBUG-68711)
5812 QQuickView view;
5813 view.setSource(testFileUrl(fileName: "keys_shortcutoverride.qml"));
5814 view.show();
5815 view.requestActivate();
5816 QVERIFY(QTest::qWaitForWindowActive(&view));
5817 QObject *root = view.rootObject();
5818 QVERIFY(root);
5819
5820 QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit*>();
5821 QVERIFY(textEdit);
5822 QQuickRectangle *rectangle = root->findChild<QQuickRectangle*>(aName: QLatin1String("rectangle"));
5823 QVERIFY(rectangle);
5824
5825 // Precondition: check if its not already changed
5826 QCOMPARE(root->property("who").value<QString>(), QLatin1String("nobody"));
5827
5828 // send Key_Escape to the Rectangle
5829 QVERIFY(rectangle->hasActiveFocus());
5830 QTest::keyPress(window: &view, key: Qt::Key_Escape);
5831 QCOMPARE(root->property("who").value<QString>(), QLatin1String("Rectangle"));
5832
5833 // send Key_Escape to TextEdit
5834 textEdit->setFocus(true);
5835 QTest::keyPress(window: &view, key: Qt::Key_Escape);
5836 QCOMPARE(root->property("who").value<QString>(), QLatin1String("TextEdit"));
5837}
5838
5839void tst_qquicktextedit::transparentSelectionColor()
5840{
5841 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
5842 || (QGuiApplication::platformName() == QLatin1String("minimal")))
5843 QSKIP("Skipping due to grabToImage not functional on offscreen/minimal platforms");
5844
5845 QQuickView view;
5846 view.setSource(testFileUrl(fileName: "transparentSelectionColor.qml"));
5847 view.show();
5848 QVERIFY(QTest::qWaitForWindowExposed(&view));
5849 QObject *root = view.rootObject();
5850 QVERIFY(root);
5851
5852 QQuickTextEdit *textEdit = root->findChild<QQuickTextEdit *>();
5853 QVERIFY(textEdit);
5854 textEdit->selectAll();
5855
5856 QImage img = view.grabWindow();
5857 QCOMPARE(img.isNull(), false);
5858
5859 QColor color = img.pixelColor(x: int(textEdit->width() / 2), y: int(textEdit->height()) / 2);
5860 QVERIFY(color.red() > 250);
5861 QVERIFY(color.blue() < 10);
5862 QVERIFY(color.green() < 10);
5863}
5864
5865QTEST_MAIN(tst_qquicktextedit)
5866
5867#include "tst_qquicktextedit.moc"
5868

source code of qtdeclarative/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp