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 QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquicktextnode_p.h"
41
42#include "qquicktextnodeengine_p.h"
43
44#include <private/qsgadaptationlayer_p.h>
45#include <private/qsgdistancefieldglyphnode_p.h>
46#include <private/qquickclipnode_p.h>
47#include <private/qquickitem_p.h>
48#include <QtQuick/private/qsgcontext_p.h>
49
50#include <QtCore/qpoint.h>
51#include <qtextdocument.h>
52#include <qtextlayout.h>
53#include <qabstracttextdocumentlayout.h>
54#include <qxmlstream.h>
55#include <private/qquickstyledtext_p.h>
56#include <private/qfont_p.h>
57#include <private/qfontengine_p.h>
58
59#include <private/qtextdocumentlayout_p.h>
60#include <qhash.h>
61
62QT_BEGIN_NAMESPACE
63
64namespace {
65
66 class ProtectedLayoutAccessor: public QAbstractTextDocumentLayout
67 {
68 public:
69 inline QTextCharFormat formatAccessor(int pos)
70 {
71 return format(pos);
72 }
73 };
74
75}
76
77/*!
78 Creates an empty QQuickTextNode
79*/
80QQuickTextNode::QQuickTextNode(QQuickItem *ownerElement)
81 : m_cursorNode(nullptr), m_ownerElement(ownerElement), m_useNativeRenderer(false)
82{
83#ifdef QSG_RUNTIME_DESCRIPTION
84 qsgnode_set_description(node: this, description: QLatin1String("text"));
85#endif
86}
87
88QQuickTextNode::~QQuickTextNode()
89{
90 qDeleteAll(c: m_textures);
91}
92
93QSGGlyphNode *QQuickTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
94 QQuickText::TextStyle style, const QColor &styleColor,
95 QSGNode *parentNode)
96{
97 QSGRenderContext *sg = QQuickItemPrivate::get(item: m_ownerElement)->sceneGraphRenderContext();
98 QRawFont font = glyphs.rawFont();
99 bool preferNativeGlyphNode = m_useNativeRenderer;
100 if (!preferNativeGlyphNode) {
101 QRawFontPrivate *fontPriv = QRawFontPrivate::get(font);
102 if (fontPriv->fontEngine->hasUnreliableGlyphOutline()) {
103 preferNativeGlyphNode = true;
104 } else {
105 QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
106 preferNativeGlyphNode = !fe->isSmoothlyScalable;
107 }
108 }
109
110 QSGGlyphNode *node = sg->sceneGraphContext()->createGlyphNode(rc: sg, preferNativeGlyphNode);
111
112 node->setOwnerElement(m_ownerElement);
113 node->setGlyphs(position: position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
114 node->setStyle(style);
115 node->setStyleColor(styleColor);
116 node->setColor(color);
117 node->update();
118
119 /* We flag the geometry as static, but we never call markVertexDataDirty
120 or markIndexDataDirty on them. This is because all text nodes are
121 discarded when a change occurs. If we start appending/removing from
122 existing geometry, then we also need to start marking the geometry as
123 dirty.
124 */
125 node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
126 node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
127
128 if (parentNode == nullptr)
129 parentNode = this;
130 parentNode->appendChildNode(node);
131
132 if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
133 QSGGlyphNode *fillNode = sg->sceneGraphContext()->createGlyphNode(rc: sg, preferNativeGlyphNode);
134 fillNode->setOwnerElement(m_ownerElement);
135 fillNode->setGlyphs(position: position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
136 fillNode->setStyle(QQuickText::Normal);
137 fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
138 fillNode->setColor(color);
139 fillNode->update();
140
141 fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
142 fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
143
144 parentNode->appendChildNode(node: fillNode);
145 fillNode->setRenderOrder(node->renderOrder() + 1);
146 }
147
148 return node;
149}
150
151void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
152{
153 if (m_cursorNode != nullptr)
154 delete m_cursorNode;
155
156 QSGRenderContext *sg = QQuickItemPrivate::get(item: m_ownerElement)->sceneGraphRenderContext();
157 m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, c: color);
158 appendChildNode(node: m_cursorNode);
159}
160
161void QQuickTextNode::clearCursor()
162{
163 if (m_cursorNode)
164 removeChildNode(node: m_cursorNode);
165 delete m_cursorNode;
166 m_cursorNode = nullptr;
167}
168
169void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
170{
171 QSGRenderContext *sg = QQuickItemPrivate::get(item: m_ownerElement)->sceneGraphRenderContext();
172 appendChildNode(node: sg->sceneGraphContext()->createInternalRectangleNode(rect, c: color));
173}
174
175
176void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
177{
178 QSGRenderContext *sg = QQuickItemPrivate::get(item: m_ownerElement)->sceneGraphRenderContext();
179 QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode(renderContext: sg);
180 QSGTexture *texture = sg->createTexture(image);
181 if (m_ownerElement->smooth())
182 texture->setFiltering(QSGTexture::Linear);
183 m_textures.append(t: texture);
184 node->setTargetRect(rect);
185 node->setInnerTargetRect(rect);
186 node->setTexture(texture);
187 if (m_ownerElement->smooth())
188 node->setFiltering(QSGTexture::Linear);
189 appendChildNode(node);
190 node->update();
191}
192
193void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument,
194 const QColor &textColor,
195 QQuickText::TextStyle style, const QColor &styleColor,
196 const QColor &anchorColor,
197 const QColor &selectionColor, const QColor &selectedTextColor,
198 int selectionStart, int selectionEnd)
199{
200 QQuickTextNodeEngine engine;
201 engine.setTextColor(textColor);
202 engine.setSelectedTextColor(selectedTextColor);
203 engine.setSelectionColor(selectionColor);
204 engine.setAnchorColor(anchorColor);
205 engine.setPosition(position);
206
207 QList<QTextFrame *> frames;
208 frames.append(t: textDocument->rootFrame());
209 while (!frames.isEmpty()) {
210 QTextFrame *textFrame = frames.takeFirst();
211 frames.append(t: textFrame->childFrames());
212
213 engine.addFrameDecorations(document: textDocument, frame: textFrame);
214
215 if (textFrame->firstPosition() > textFrame->lastPosition()
216 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
217 const int pos = textFrame->firstPosition() - 1;
218 ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(textDocument->documentLayout());
219 QTextCharFormat format = a->formatAccessor(pos);
220 QRectF rect = a->frameBoundingRect(frame: textFrame);
221
222 QTextBlock block = textFrame->firstCursorPosition().block();
223 engine.setCurrentLine(block.layout()->lineForTextPosition(pos: pos - block.position()));
224 engine.addTextObject(block, position: rect.topLeft(), format, selectionState: QQuickTextNodeEngine::Unselected, textDocument,
225 pos, layoutPosition: textFrame->frameFormat().position());
226 } else {
227 QTextFrame::iterator it = textFrame->begin();
228
229 while (!it.atEnd()) {
230 Q_ASSERT(!engine.currentLine().isValid());
231
232 QTextBlock block = it.currentBlock();
233 engine.addTextBlock(textDocument, block, position, textColor, anchorColor, selectionStart, selectionEnd);
234 ++it;
235 }
236 }
237 }
238
239 engine.addToSceneGraph(parent: this, style, styleColor);
240}
241
242void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
243 QQuickText::TextStyle style, const QColor &styleColor,
244 const QColor &anchorColor,
245 const QColor &selectionColor, const QColor &selectedTextColor,
246 int selectionStart, int selectionEnd,
247 int lineStart, int lineCount)
248{
249 QQuickTextNodeEngine engine;
250 engine.setTextColor(color);
251 engine.setSelectedTextColor(selectedTextColor);
252 engine.setSelectionColor(selectionColor);
253 engine.setAnchorColor(anchorColor);
254 engine.setPosition(position);
255
256#if QT_CONFIG(im)
257 int preeditLength = textLayout->preeditAreaText().length();
258 int preeditPosition = textLayout->preeditAreaPosition();
259#endif
260
261 QVarLengthArray<QTextLayout::FormatRange> colorChanges;
262 engine.mergeFormats(textLayout, mergedFormats: &colorChanges);
263
264 lineCount = lineCount >= 0
265 ? qMin(a: lineStart + lineCount, b: textLayout->lineCount())
266 : textLayout->lineCount();
267
268 for (int i=lineStart; i<lineCount; ++i) {
269 QTextLine line = textLayout->lineAt(i);
270
271 int start = line.textStart();
272 int length = line.textLength();
273 int end = start + length;
274
275#if QT_CONFIG(im)
276 if (preeditPosition >= 0
277 && preeditPosition >= start
278 && preeditPosition < end) {
279 end += preeditLength;
280 }
281#endif
282
283 engine.setCurrentLine(line);
284 engine.addGlyphsForRanges(ranges: colorChanges, start, end, selectionStart, selectionEnd);
285 }
286
287 engine.addToSceneGraph(parent: this, style, styleColor);
288}
289
290void QQuickTextNode::deleteContent()
291{
292 while (firstChild() != nullptr)
293 delete firstChild();
294 m_cursorNode = nullptr;
295 qDeleteAll(c: m_textures);
296 m_textures.clear();
297}
298
299QT_END_NAMESPACE
300

source code of qtdeclarative/src/quick/items/qquicktextnode.cpp