aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items')
-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
4 files changed, 254 insertions, 3 deletions
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();