diff options
-rw-r--r-- | examples/quickcontrols/texteditor/qml/texteditor.qml | 54 | ||||
-rw-r--r-- | src/quick/items/qquicktextdocument.cpp | 234 | ||||
-rw-r--r-- | src/quick/items/qquicktextdocument.h | 17 | ||||
-rw-r--r-- | src/quick/items/qquicktextdocument_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/qquicktextedit.cpp | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/hello-8857-7.html | 9 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/hello-utf16be.html | bin | 0 -> 48 bytes | |||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/hello.html | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/hello.md | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/hello.txt | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/data/text.qml | 12 | ||||
-rw-r--r-- | tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp | 99 |
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 Binary files differnew file mode 100644 index 0000000000..be84e0d63c --- /dev/null +++ b/tests/auto/quick/qquicktextdocument/data/hello-utf16be.html 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" |