1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquicktextdocument.h" |
5 | #include "qquicktextdocument_p.h" |
6 | |
7 | #include "qquicktextedit_p.h" |
8 | #include "qquicktextedit_p_p.h" |
9 | #include "qquicktext_p_p.h" |
10 | |
11 | #include <QtQml/qqmlinfo.h> |
12 | #include <QtQml/qqmlcontext.h> |
13 | #include <QtQuick/private/qquickpixmapcache_p.h> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | /*! |
18 | \class QQuickTextDocument |
19 | \since 5.1 |
20 | \brief The QQuickTextDocument class provides access to the QTextDocument of QQuickTextEdit. |
21 | \inmodule QtQuick |
22 | |
23 | This class provides access to the QTextDocument of QQuickTextEdit elements. |
24 | This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt. |
25 | You are not allowed to modify the document, but it can be used to output content, for example with \l{QTextDocumentWriter}), |
26 | or provide additional formatting, for example with \l{QSyntaxHighlighter}. |
27 | |
28 | The class has to be used from C++ directly, using the property of the \l TextEdit. |
29 | |
30 | Warning: The QTextDocument provided is used internally by \l {Qt Quick} elements to provide text manipulation primitives. |
31 | You are not allowed to perform any modification of the internal state of the QTextDocument. If you do, the element |
32 | in question may stop functioning or crash. |
33 | */ |
34 | |
35 | class QQuickTextDocumentPrivate : public QObjectPrivate |
36 | { |
37 | public: |
38 | QPointer<QTextDocument> document; |
39 | }; |
40 | |
41 | /*! |
42 | Constructs a QQuickTextDocument object with |
43 | \a parent as the parent object. |
44 | */ |
45 | QQuickTextDocument::QQuickTextDocument(QQuickItem *parent) |
46 | : QObject(*(new QQuickTextDocumentPrivate), parent) |
47 | { |
48 | Q_D(QQuickTextDocument); |
49 | Q_ASSERT(parent); |
50 | Q_ASSERT(qobject_cast<QQuickTextEdit*>(parent)); |
51 | d->document = QPointer<QTextDocument>(qobject_cast<QQuickTextEdit*>(object: parent)->d_func()->document); |
52 | } |
53 | |
54 | /*! |
55 | Returns a pointer to the QTextDocument object. |
56 | */ |
57 | QTextDocument* QQuickTextDocument::textDocument() const |
58 | { |
59 | Q_D(const QQuickTextDocument); |
60 | return d->document.data(); |
61 | } |
62 | |
63 | QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent) |
64 | : QTextDocument(parent), outstanding(0) |
65 | { |
66 | setUndoRedoEnabled(false); |
67 | documentLayout()->registerHandler(objectType: QTextFormat::ImageObject, component: this); |
68 | connect(sender: this, signal: &QTextDocument::baseUrlChanged, slot: [this]() { |
69 | clearResources(); |
70 | markContentsDirty(from: 0, length: characterCount()); |
71 | }); |
72 | } |
73 | |
74 | QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources() |
75 | { |
76 | if (!m_resources.isEmpty()) |
77 | qDeleteAll(c: m_resources); |
78 | } |
79 | |
80 | QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name) |
81 | { |
82 | QVariant resource = QTextDocument::loadResource(type, name); |
83 | if (resource.isNull() && type == QTextDocument::ImageResource) { |
84 | QQmlContext *context = qmlContext(parent()); |
85 | QUrl url = baseUrl().resolved(relative: name); |
86 | QQuickPixmap *p = loadPixmap(context, name: url); |
87 | resource = p->image(); |
88 | } |
89 | |
90 | return resource; |
91 | } |
92 | |
93 | void QQuickTextDocumentWithImageResources::requestFinished() |
94 | { |
95 | outstanding--; |
96 | if (outstanding == 0) { |
97 | markContentsDirty(from: 0, length: characterCount()); |
98 | emit imagesLoaded(); |
99 | } |
100 | } |
101 | |
102 | QSizeF QQuickTextDocumentWithImageResources::intrinsicSize( |
103 | QTextDocument *, int, const QTextFormat &format) |
104 | { |
105 | if (format.isImageFormat()) { |
106 | QTextImageFormat imageFormat = format.toImageFormat(); |
107 | |
108 | const int width = qRound(d: imageFormat.width()); |
109 | const bool hasWidth = imageFormat.hasProperty(propertyId: QTextFormat::ImageWidth) && width > 0; |
110 | const int height = qRound(d: imageFormat.height()); |
111 | const bool hasHeight = imageFormat.hasProperty(propertyId: QTextFormat::ImageHeight) && height > 0; |
112 | |
113 | QSizeF size(width, height); |
114 | if (!hasWidth || !hasHeight) { |
115 | QVariant res = resource(type: QTextDocument::ImageResource, name: QUrl(imageFormat.name())); |
116 | QImage image = res.value<QImage>(); |
117 | if (image.isNull()) { |
118 | if (!hasWidth) |
119 | size.setWidth(16); |
120 | if (!hasHeight) |
121 | size.setHeight(16); |
122 | return size; |
123 | } |
124 | QSize imgSize = image.size(); |
125 | |
126 | if (!hasWidth) { |
127 | if (!hasHeight) |
128 | size.setWidth(imgSize.width()); |
129 | else |
130 | size.setWidth(qRound(d: height * (imgSize.width() / (qreal) imgSize.height()))); |
131 | } |
132 | if (!hasHeight) { |
133 | if (!hasWidth) |
134 | size.setHeight(imgSize.height()); |
135 | else |
136 | size.setHeight(qRound(d: width * (imgSize.height() / (qreal) imgSize.width()))); |
137 | } |
138 | } |
139 | return size; |
140 | } |
141 | return QSizeF(); |
142 | } |
143 | |
144 | void QQuickTextDocumentWithImageResources::drawObject( |
145 | QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &) |
146 | { |
147 | } |
148 | |
149 | QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format) const |
150 | { |
151 | QVariant res = resource(type: QTextDocument::ImageResource, name: QUrl(format.name())); |
152 | return res.value<QImage>(); |
153 | } |
154 | |
155 | QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap( |
156 | QQmlContext *context, const QUrl &url) |
157 | { |
158 | |
159 | QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(key: url); |
160 | |
161 | if (iter == m_resources.end()) { |
162 | QQuickPixmap *p = new QQuickPixmap(context->engine(), url); |
163 | iter = m_resources.insert(key: url, value: p); |
164 | |
165 | if (p->isLoading()) { |
166 | p->connectFinished(this, SLOT(requestFinished())); |
167 | outstanding++; |
168 | } |
169 | } |
170 | |
171 | QQuickPixmap *p = *iter; |
172 | if (p->isError()) { |
173 | if (!errors.contains(value: url)) { |
174 | errors.insert(value: url); |
175 | qmlWarning(me: parent()) << p->error(); |
176 | } |
177 | } |
178 | return p; |
179 | } |
180 | |
181 | void QQuickTextDocumentWithImageResources::clearResources() |
182 | { |
183 | for (QQuickPixmap *pixmap : std::as_const(t&: m_resources)) |
184 | pixmap->clear(this); |
185 | qDeleteAll(c: m_resources); |
186 | m_resources.clear(); |
187 | outstanding = 0; |
188 | } |
189 | |
190 | QSet<QUrl> QQuickTextDocumentWithImageResources::errors; |
191 | |
192 | QT_END_NAMESPACE |
193 | |
194 | #include "moc_qquicktextdocument.cpp" |
195 | #include "moc_qquicktextdocument_p.cpp" |
196 | |