aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quickcontrols/texteditor/qml/texteditor.qml54
-rw-r--r--src/quick/items/qquicktextdocument.cpp234
-rw-r--r--src/quick/items/qquicktextdocument.h17
-rw-r--r--src/quick/items/qquicktextdocument_p.h5
-rw-r--r--src/quick/items/qquicktextedit.cpp1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello-8857-7.html9
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello-utf16be.htmlbin0 -> 48 bytes
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.html1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.md1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/hello.txt1
-rw-r--r--tests/auto/quick/qquicktextdocument/data/text.qml12
-rw-r--r--tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp99
12 files changed, 418 insertions, 16 deletions
diff --git a/examples/quickcontrols/texteditor/qml/texteditor.qml b/examples/quickcontrols/texteditor/qml/texteditor.qml
index decf0801e4..c6a53d8c1b 100644
--- a/examples/quickcontrols/texteditor/qml/texteditor.qml
+++ b/examples/quickcontrols/texteditor/qml/texteditor.qml
@@ -18,7 +18,8 @@ ApplicationWindow {
width: 1024
height: 600
visible: true
- title: document.fileName + " - Text Editor Example"
+ title: textArea.textDocument.source +
+ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
Component.onCompleted: {
x = Screen.width / 2 - width / 2
@@ -28,7 +29,18 @@ ApplicationWindow {
Action {
id: openAction
shortcut: StandardKey.Open
- onTriggered: openDialog.open()
+ onTriggered: {
+ if (textArea.textDocument.modified)
+ discardDialog.open()
+ else
+ openDialog.open()
+ }
+ }
+
+ Action {
+ id: saveAction
+ shortcut: StandardKey.Save
+ onTriggered: textArea.textDocument.save()
}
Action {
@@ -153,17 +165,18 @@ ApplicationWindow {
selectedNameFilter.index: 1
nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)", "Markdown files (*.md *.markdown)"]
currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: document.load(selectedFile)
+ onAccepted: {
+ textArea.textDocument.modified = false // we asked earlier, if necessary
+ textArea.textDocument.source = selectedFile
+ }
}
FileDialog {
id: saveDialog
fileMode: FileDialog.SaveFile
- defaultSuffix: document.fileType
nameFilters: openDialog.nameFilters
- selectedNameFilter.index: document.fileType === "txt" ? 0 : 1
currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: document.saveAs(selectedFile)
+ onAccepted: textArea.textDocument.saveAs(selectedFile)
}
FontDialog {
@@ -190,6 +203,17 @@ ApplicationWindow {
onButtonClicked: function (button, role) { if (role === MessageDialog.YesRole) Qt.quit() }
}
+ MessageDialog {
+ id : discardDialog
+ title: qsTr("Discard changes?")
+ text: qsTr("The file has been modified. Open a new file anyway?")
+ buttons: MessageDialog.Yes | MessageDialog.No
+ onButtonClicked: function (button, role) {
+ if (role === MessageDialog.YesRole)
+ openDialog.open()
+ }
+ }
+
header: ToolBar {
leftPadding: 8
@@ -206,6 +230,13 @@ ApplicationWindow {
action: openAction
focusPolicy: Qt.TabFocus
}
+ ToolButton {
+ id: saveButton
+ text: "\uE80A" // icon-floppy-disk
+ font.family: "fontello"
+ action: saveAction
+ focusPolicy: Qt.TabFocus
+ }
ToolSeparator {
contentItem.visible: fileRow.y === editRow.y
}
@@ -383,9 +414,9 @@ ApplicationWindow {
Component.onCompleted: {
if (Qt.application.arguments.length === 2)
- document.load("file:" + Qt.application.arguments[1]);
+ textArea.textDocument.source = "file:" + Qt.application.arguments[1];
else
- document.load("qrc:/texteditor.html")
+ textArea.textDocument.source = "qrc:/texteditor.html";
}
onLoaded: function (text, format) {
textArea.textFormat = format
@@ -427,6 +458,11 @@ ApplicationWindow {
onLinkActivated: function (link) {
Qt.openUrlExternally(link)
}
+
+ textDocument.onError: function (message) {
+ errorDialog.text = message
+ errorDialog.open()
+ }
}
ScrollBar.vertical: ScrollBar {}
@@ -471,7 +507,7 @@ ApplicationWindow {
}
onClosing: function (close) {
- if (document.modified) {
+ if (textArea.textDocument.modified) {
quitDialog.open()
close.accepted = false
}
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 81298596b0..432a69b494 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -12,10 +12,33 @@
#include <QtQml/qqmlcontext.h>
#include <QtQuick/private/qquickpixmap_p.h>
+#include <QtCore/qmimedatabase.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+/*!
+ \qmltype TextDocument
+ \instantiates QQuickTextDocument
+ \inqmlmodule QtQuick
+ \brief A wrapper around TextEdit's backing QTextDocument.
+
+ To load text into the document, set the \l source property. If the user then
+ modifies the text and wants to save the same document, call \l save() to save
+ it to the same source again (only if \l {QUrl::isLocalFile()}{it's a local file}).
+ Or call \l saveAs() to save it to a different file.
+
+ This class cannot be instantiated in QML, but is available from \l TextEdit::textDocument.
+
+ \note All loading and saving is done synchronously for now.
+ This may block the UI if the \l source is a slow network drive.
+ This may be improved in future versions of Qt.
+
+ \note This API is considered tech preview and may change in future versions of Qt.
+*/
+
/*!
\class QQuickTextDocument
\since 5.1
@@ -27,8 +50,6 @@ QT_BEGIN_NAMESPACE
including document modifications. It can also be used to output content,
for example with \l{QTextDocumentWriter}), or provide additional formatting,
for example with \l{QSyntaxHighlighter}.
-
- This class cannot be instantiated in QML, but is available from \l TextEdit::textDocument.
*/
/*!
@@ -42,6 +63,146 @@ QQuickTextDocument::QQuickTextDocument(QQuickItem *parent)
Q_ASSERT(parent);
d->editor = qobject_cast<QQuickTextEdit *>(parent);
Q_ASSERT(d->editor);
+ connect(textDocument(), &QTextDocument::modificationChanged,
+ this, &QQuickTextDocument::modifiedChanged);
+}
+
+/*!
+ \qmlproperty url QtQuick::TextDocument::source
+ \since 6.7
+
+ QQuickTextDocument can handle any text format supported by Qt, loaded from
+ any URL scheme supported by Qt.
+
+ The URL may be absolute, or relative to the URL of the component.
+
+ The \c source property cannot be changed while the document's \l modified
+ state is \c true. If the user has modified the document contents, you
+ should prompt the user whether to \l save(), or else discard changes by
+ setting \c {modified = false} before setting the \l source property to a
+ different URL.
+
+ \sa QTextDocumentWriter::supportedDocumentFormats()
+*/
+QUrl QQuickTextDocument::source() const
+{
+ Q_D(const QQuickTextDocument);
+ return d->url;
+}
+
+void QQuickTextDocument::setSource(const QUrl &url)
+{
+ Q_D(QQuickTextDocument);
+
+ if (url == d->url)
+ return;
+
+ if (isModified()) {
+ qmlWarning(this) << "Existing document modified: you should save(),"
+ "or call TextEdit.clear() before setting a different source";
+ return;
+ }
+
+ d->url = url;
+ emit sourceChanged();
+ d->load();
+}
+
+/*!
+ \qmlproperty bool QtQuick::TextDocument::modified
+ \since 6.7
+
+ This property holds whether the document has been modified by the user
+ since the last time it was loaded or saved. By default, this property is
+ \c false.
+
+ As with \l QTextDocument::modified, you can set the modified property:
+ for example, set it to \c false to allow setting the \c source property
+ to a different URL (thus discarding the user's changes).
+
+ \sa QTextDocument::modified
+*/
+bool QQuickTextDocument::isModified() const
+{
+ const auto *doc = textDocument();
+ return doc && doc->isModified();
+}
+
+void QQuickTextDocument::setModified(bool modified)
+{
+ if (auto *doc = textDocument())
+ doc->setModified(modified);
+}
+
+void QQuickTextDocumentPrivate::load()
+{
+ Q_Q(QQuickTextDocument);
+ const QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
+ if (QFile::exists(fileName)) {
+ mimeType = QMimeDatabase().mimeTypeForFile(fileName);
+ QFile file(fileName);
+ if (file.open(QFile::ReadOnly)) {
+ QByteArray data = file.readAll();
+ if (auto *doc = editor->document()) {
+ doc->setBaseUrl(url.adjusted(QUrl::RemoveFilename));
+ if (mimeType.inherits("text/markdown"_L1)) {
+ doc->setMarkdown(QString::fromUtf8(data));
+ } else if (mimeType.inherits("text/html"_L1)) {
+ // If a user loads an HTML file, remember the encoding.
+ // If the user then calls save() later, the same encoding will be used.
+ encoding = QStringConverter::encodingForHtml(data);
+ if (encoding) {
+ QStringDecoder decoder(*encoding);
+ doc->setHtml(decoder(data));
+ } else {
+ // fall back to utf8
+ doc->setHtml(QString::fromUtf8(data));
+ }
+ } else {
+ doc->setPlainText(QString::fromUtf8(data));
+ }
+ doc->setModified(false);
+ }
+ } else {
+ emit q->error(QQuickTextDocument::tr("Cannot load: ") + file.errorString());
+ }
+ }
+}
+
+void QQuickTextDocumentPrivate::writeTo(const QUrl &fileUrl)
+{
+ Q_Q(QQuickTextDocument);
+ auto *doc = editor->document();
+ if (!doc)
+ return;
+
+ const QString filePath = fileUrl.toLocalFile();
+ const bool sameUrl = fileUrl == url;
+ const auto type = (sameUrl ? mimeType : QMimeDatabase().mimeTypeForUrl(fileUrl));
+ const bool isHtml = type.inherits("text/html"_L1);
+ QFile file(filePath);
+ if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
+ emit q->error(QQuickTextDocument::tr("Cannot save: ") + file.errorString());
+ return;
+ }
+ QByteArray raw;
+ if (type.inherits("text/markdown"_L1)) {
+ raw = doc->toMarkdown().toUtf8();
+ } else if (isHtml) {
+ if (sameUrl && encoding) {
+ QStringEncoder enc(*encoding);
+ raw = enc.encode(doc->toHtml());
+ } else {
+ // default to UTF-8 unless the user is saving the same file as previously loaded
+ raw = doc->toHtml().toUtf8();
+ }
+ } else {
+ raw = doc->toPlainText().toUtf8();
+ }
+
+ file.write(raw);
+ file.close();
+ doc->setModified(false);
}
QTextDocument *QQuickTextDocumentPrivate::document() const
@@ -52,9 +213,16 @@ QTextDocument *QQuickTextDocumentPrivate::document() const
void QQuickTextDocumentPrivate::setDocument(QTextDocument *doc)
{
Q_Q(QQuickTextDocument);
- if (doc == editor->document())
+ QTextDocument *oldDoc = editor->document();
+ if (doc == oldDoc)
return;
+ if (oldDoc)
+ oldDoc->disconnect(q);
+ if (doc) {
+ q->connect(doc, &QTextDocument::modificationChanged,
+ q, &QQuickTextDocument::modifiedChanged);
+ }
editor->setDocument(doc);
emit q->textDocumentChanged();
}
@@ -79,6 +247,59 @@ void QQuickTextDocument::setTextDocument(QTextDocument *document)
d_func()->setDocument(document);
}
+/*!
+ \fn void QQuickTextDocument::textDocumentChanged()
+ \since 6.7
+
+ This signal is emitted when the underlying QTextDocument is
+ replaced with a different instance.
+
+ \sa setTextDocument()
+*/
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::save()
+ \brief Saves the contents to the same file and format specified by \l source.
+ \since 6.7
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, saveAs()
+*/
+void QQuickTextDocument::save()
+{
+ Q_D(QQuickTextDocument);
+ d->writeTo(d->url);
+}
+
+/*!
+ \qmlmethod void QtQuick::TextDocument::saveAs(url url)
+ \brief Saves the contents to the file and format specified by \a url.
+ \since 6.7
+
+ The file extension in \a url specifies the file format
+ (as determined by QMimeDatabase::mimeTypeForUrl()).
+
+ \note You can save only to a \l {QUrl::isLocalFile()}{file on a mounted filesystem}.
+
+ \sa source, saveAs()
+*/
+void QQuickTextDocument::saveAs(const QUrl &url)
+{
+ Q_D(QQuickTextDocument);
+ if (!url.isLocalFile()) {
+ emit error(tr("Can only save to local files"));
+ return;
+ }
+ d->writeTo(url);
+
+ if (url == d->url)
+ return;
+
+ d->url = url;
+ emit sourceChanged();
+}
+
QQuickTextImageHandler::QQuickTextImageHandler(QObject *parent)
: QObject(parent)
{
@@ -125,6 +346,13 @@ QSizeF QQuickTextImageHandler::intrinsicSize(
return QSizeF();
}
+/*!
+ \qmlsignal QtQuick::TextDocument::error(string message)
+
+ This signal is emitted when an error \a message (translated string) should
+ be presented to the user, for example with a MessageDialog.
+*/
+
QT_END_NAMESPACE
#include "moc_qquicktextdocument.cpp"
diff --git a/src/quick/items/qquicktextdocument.h b/src/quick/items/qquicktextdocument.h
index 5ed612b8ae..b2e8137c0a 100644
--- a/src/quick/items/qquicktextdocument.h
+++ b/src/quick/items/qquicktextdocument.h
@@ -13,16 +13,33 @@ class QQuickTextDocumentPrivate;
class Q_QUICK_EXPORT QQuickTextDocument : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged REVISION(6, 7) FINAL)
+ Q_PROPERTY(bool modified READ isModified WRITE setModified NOTIFY modifiedChanged REVISION(6, 7) FINAL)
+
QML_ANONYMOUS
QML_ADDED_IN_VERSION(2, 0)
public:
QQuickTextDocument(QQuickItem *parent);
+
+ QUrl source() const;
+ void setSource(const QUrl &url);
+
+ bool isModified() const;
+ void setModified(bool modified);
+
QTextDocument *textDocument() const;
void setTextDocument(QTextDocument *document);
+ Q_REVISION(6, 7) Q_INVOKABLE void save();
+ Q_REVISION(6, 7) Q_INVOKABLE void saveAs(const QUrl &url);
+
Q_SIGNALS:
Q_REVISION(6,7) void textDocumentChanged();
+ Q_REVISION(6, 7) void sourceChanged();
+ Q_REVISION(6, 7) void modifiedChanged();
+
+ Q_REVISION(6, 7) void error(const QString &message);
private:
Q_DISABLE_COPY(QQuickTextDocument)
diff --git a/src/quick/items/qquicktextdocument_p.h b/src/quick/items/qquicktextdocument_p.h
index 04aa06bb97..8c5bc07a0a 100644
--- a/src/quick/items/qquicktextdocument_p.h
+++ b/src/quick/items/qquicktextdocument_p.h
@@ -60,11 +60,16 @@ public:
static QQuickTextDocumentPrivate *get(QQuickTextDocument *doc) { return doc->d_func(); }
static const QQuickTextDocumentPrivate *get(const QQuickTextDocument *doc) { return doc->d_func(); }
+ void load();
+ void writeTo(const QUrl &fileUrl);
QTextDocument *document() const;
void setDocument(QTextDocument *doc);
// so far the QQuickItem given to the QQuickTextDocument ctor is always a QQuickTextEdit
QQuickTextEdit *editor = nullptr;
+ QUrl url;
+ QMimeType mimeType;
+ std::optional<QStringConverter::Encoding> encoding; // only relevant for HTML
};
namespace QtPrivate {
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 9185c11848..ede599cfa3 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -2646,6 +2646,7 @@ void QQuickTextEditPrivate::init()
document->setUndoRedoEnabled(false); // flush undo buffer.
document->setUndoRedoEnabled(true);
updateDefaultTextOption();
+ document->setModified(false); // we merely changed some defaults: no edits worth saving yet
q->updateSize();
#if QT_CONFIG(cursor)
updateMouseCursorShape();
diff --git a/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html b/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html
new file mode 100644
index 0000000000..6bf900858a
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello-8857-7.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="iso-8859-7">
+</head>
+<body>
+<p>���� ��� �����!</p>
+</body>
+</html>
diff --git a/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html b/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html
new file mode 100644
index 0000000000..be84e0d63c
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html
Binary files differ
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.html b/tests/auto/quick/qquicktextdocument/data/hello.html
new file mode 100644
index 0000000000..c160cd5e63
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.html
@@ -0,0 +1 @@
+Γειά σου <i>Κόσμε</i>!
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.md b/tests/auto/quick/qquicktextdocument/data/hello.md
new file mode 100644
index 0000000000..290f3f1397
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.md
@@ -0,0 +1 @@
+Γειά σου *Κόσμε*!
diff --git a/tests/auto/quick/qquicktextdocument/data/hello.txt b/tests/auto/quick/qquicktextdocument/data/hello.txt
new file mode 100644
index 0000000000..1f72c3f7a4
--- /dev/null
+++ b/tests/auto/quick/qquicktextdocument/data/hello.txt
@@ -0,0 +1 @@
+Γειά σου Κόσμε!
diff --git a/tests/auto/quick/qquicktextdocument/data/text.qml b/tests/auto/quick/qquicktextdocument/data/text.qml
index 43a8c6bb82..ac7b8fa51f 100644
--- a/tests/auto/quick/qquicktextdocument/data/text.qml
+++ b/tests/auto/quick/qquicktextdocument/data/text.qml
@@ -1,6 +1,12 @@
-import QtQuick 2.1
+import QtQuick
TextEdit {
- text: ""
-}
+ id: te
+ property int sourceChangeCount: 0
+ property int modifiedChangeCount: 0
+
+ text: "" // this is not a document modification
+ textDocument.onSourceChanged: ++te.sourceChangeCount
+ textDocument.onModifiedChanged: ++te.modifiedChangeCount
+}
diff --git a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
index 343eadad5e..1d5175e1c8 100644
--- a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
+++ b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp
@@ -5,17 +5,22 @@
#include <QtTest/QtTest>
#include <QtQuick/QQuickTextDocument>
#include <QtQuick/QQuickItem>
+#include <QtQuick/private/qquicktextdocument_p.h>
#include <QtQuick/private/qquicktextedit_p.h>
#include <QtQuick/private/qquicktextedit_p_p.h>
+#include <QtCore/QStringConverter>
#include <QtGui/QTextBlock>
#include <QtGui/QTextDocument>
#include <QtGui/QTextDocumentWriter>
-#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlFile>
#include <QtQuickTestUtils/private/qmlutils_p.h>
Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
+using namespace Qt::StringLiterals;
+
class tst_qquicktextdocument : public QQmlDataTest
{
Q_OBJECT
@@ -25,6 +30,8 @@ public:
private slots:
void textDocumentWriter();
void customDocument();
+ void sourceAndSave_data();
+ void sourceAndSave();
};
QString text = QStringLiteral("foo bar");
@@ -147,6 +154,96 @@ void tst_qquicktextdocument::customDocument()
QCOMPARE(fragmentCount, 2);
}
+void tst_qquicktextdocument::sourceAndSave_data()
+{
+ QTest::addColumn<QUrl>("source");
+ QTest::addColumn<std::optional<QStringConverter::Encoding>>("expectedEncoding");
+ QTest::addColumn<QString>("expectedMimeType");
+ QTest::addColumn<int>("minCharCount");
+ QTest::addColumn<QString>("expectedPlainText");
+
+ const std::optional<QStringConverter::Encoding> nullEnc;
+
+ QTest::newRow("plain") << testFileUrl("hello.txt")
+ << nullEnc << "text/plain" << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("markdown") << testFileUrl("hello.md")
+ << nullEnc << "text/markdown" << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("html") << testFileUrl("hello.html")
+ << std::optional<QStringConverter::Encoding>(QStringConverter::Utf8)
+ << "text/html" << 15 << u"Γειά σου Κόσμε!"_s;
+ QTest::newRow("html-utf16be") << testFileUrl("hello-utf16be.html")
+ << std::optional<QStringConverter::Encoding>(QStringConverter::Utf16BE)
+ << "text/html" << 15 << u"Γειά σου Κόσμε!"_s;
+}
+
+void tst_qquicktextdocument::sourceAndSave()
+{
+ QFETCH(QUrl, source);
+ QFETCH(std::optional<QStringConverter::Encoding>, expectedEncoding);
+ QFETCH(QString, expectedMimeType);
+ QFETCH(int, minCharCount);
+ QFETCH(QString, expectedPlainText);
+
+ QVERIFY(source.isLocalFile());
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("text.qml"));
+ QScopedPointer<QQuickTextEdit> textEdit(qobject_cast<QQuickTextEdit*>(c.create()));
+ QCOMPARE(textEdit.isNull(), false);
+ QQuickTextDocument *qqdoc = textEdit->property("textDocument").value<QQuickTextDocument*>();
+ QVERIFY(qqdoc);
+ // text.qml has text: "" but that's not a real change; QQuickTextEdit::setText() returns early
+ // QQuickTextEditPrivate::init() also modifies defaults and then resets the modified state
+ QCOMPARE(qqdoc->isModified(), false);
+ // no stray signals should be visible to QML during init
+ QCOMPARE(textEdit->property("modifiedChangeCount").toInt(), 0);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 0);
+ QTextDocument *doc = qqdoc->textDocument();
+ QVERIFY(doc);
+ QSignalSpy sourceChangedSpy(qqdoc, &QQuickTextDocument::sourceChanged);
+ QSignalSpy modifiedChangedSpy(qqdoc, &QQuickTextDocument::modifiedChanged);
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QFile sf(QQmlFile::urlToLocalFileOrQrc(source));
+ QVERIFY(sf.exists());
+ QString tmpPath = tmpDir.filePath(source.fileName());
+ QVERIFY(sf.copy(tmpPath));
+ qCDebug(lcTests) << source << "->" << tmpDir.path() << ":" << tmpPath;
+
+ qqdoc->setProperty("source", QUrl::fromLocalFile(tmpPath));
+ QCOMPARE(sourceChangedSpy.size(), 1);
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), 1);
+ const auto *qqdp = QQuickTextDocumentPrivate::get(qqdoc);
+ QVERIFY(qqdp->mimeType.inherits(expectedMimeType));
+ const bool expectHtml = (expectedMimeType == "text/html");
+ QCOMPARE_GE(doc->characterCount(), minCharCount);
+ QCOMPARE(doc->toPlainText().trimmed(), expectedPlainText);
+ QCOMPARE(qqdp->encoding, expectedEncoding);
+
+ textEdit->setText("hello");
+ QCOMPARE(qqdoc->isModified(), false);
+ QCOMPARE_GE(textEdit->property("modifiedChangeCount").toInt(), 1);
+ QCOMPARE(textEdit->property("modifiedChangeCount").toInt(), modifiedChangedSpy.size());
+ modifiedChangedSpy.clear();
+ textEdit->insert(5, "!");
+ QCOMPARE_GE(modifiedChangedSpy.size(), 1);
+ QCOMPARE(qqdoc->isModified(), true);
+
+ qqdoc->save();
+ QFile tf(tmpPath);
+ QVERIFY(tf.open(QIODeviceBase::ReadOnly));
+ auto readBack = tf.readAll();
+ if (expectHtml) {
+ QStringDecoder dec(*expectedEncoding);
+ const QString decStr = dec(readBack);
+ QVERIFY(decStr.contains("hello!</p>"));
+ } else {
+ QVERIFY(readBack.contains("hello!"));
+ }
+ QCOMPARE(textEdit->property("sourceChangeCount").toInt(), sourceChangedSpy.size());
+}
+
QTEST_MAIN(tst_qquicktextdocument)
#include "tst_qquicktextdocument.moc"