Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsginternaltextnode.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
6
8
9#include <private/qsgadaptationlayer_p.h>
10#include <private/qsgdistancefieldglyphnode_p.h>
11#include <private/qquickclipnode_p.h>
12#include <private/qquickitem_p.h>
13#include <private/qquicktextdocument_p.h>
14
15#include <QtCore/qpoint.h>
16#include <qtextdocument.h>
17#include <qtextlayout.h>
18#include <qabstracttextdocumentlayout.h>
19#include <private/qquickstyledtext_p.h>
20#include <private/qquicktext_p_p.h>
21#include <private/qfont_p.h>
22#include <private/qfontengine_p.h>
23
24#include <private/qtextdocumentlayout_p.h>
25#include <qhash.h>
26
28
29/*!
30 Creates an empty QSGInternalTextNode
31*/
32QSGInternalTextNode::QSGInternalTextNode(QSGRenderContext *renderContext)
33 : m_renderContext(renderContext)
34{
35#ifdef QSG_RUNTIME_DESCRIPTION
36 qsgnode_set_description(this, QLatin1String("text"));
37#endif
38
39 static_assert(int(QSGTextNode::Normal) == int(QQuickText::Normal));
40 static_assert(int(QSGTextNode::Outline) == int(QQuickText::Outline));
41 static_assert(int(QSGTextNode::Raised) == int(QQuickText::Raised));
42 static_assert(int(QSGTextNode::Sunken) == int(QQuickText::Sunken));
43
44 static_assert(int(QSGTextNode::QtRendering) == int(QQuickText::QtRendering));
45 static_assert(int(QSGTextNode::NativeRendering) == int(QQuickText::NativeRendering));
46 static_assert(int(QSGTextNode::CurveRendering) == int(QQuickText::CurveRendering));
47}
48
49QSGInternalTextNode::~QSGInternalTextNode()
50{
51 qDeleteAll(m_textures);
52}
53
54QSGGlyphNode *QSGInternalTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
55 QQuickText::TextStyle style, const QColor &styleColor,
56 QSGNode *parentNode)
57{
58 QRawFont font = glyphs.rawFont();
59
60 QSGTextNode::RenderType preferredRenderType = m_renderType;
61 if (m_renderType != NativeRendering) {
62 if (const QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine)
63 if (fe->hasUnreliableGlyphOutline() || !fe->isSmoothlyScalable)
64 preferredRenderType = QSGTextNode::NativeRendering;
65 }
66
67 if (preferredRenderType == NativeRendering)
68 m_containsUnscalableGlyphs = true;
69
70 QSGGlyphNode *node = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
71 preferredRenderType,
72 m_renderTypeQuality);
73 node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
74 node->setStyle(style);
75 node->setStyleColor(styleColor);
76 node->setColor(color);
77 node->update();
78
79 /* We flag the geometry as static, but we never call markVertexDataDirty
80 or markIndexDataDirty on them. This is because all text nodes are
81 discarded when a change occurs. If we start appending/removing from
82 existing geometry, then we also need to start marking the geometry as
83 dirty.
84 */
85 node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
86 node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
87
88 if (parentNode == nullptr)
89 parentNode = this;
90 parentNode->appendChildNode(node);
91
92 if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) {
93 QSGGlyphNode *fillNode = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext,
94 preferredRenderType,
95 m_renderTypeQuality);
96 fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
97 fillNode->setStyle(QQuickText::Normal);
98 fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing);
99 fillNode->setColor(color);
100 fillNode->update();
101
102 fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
103 fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
104
105 parentNode->appendChildNode(fillNode);
106 fillNode->setRenderOrder(node->renderOrder() + 1);
107 }
108
109 return node;
110}
111
112void QSGInternalTextNode::setCursor(const QRectF &rect, const QColor &color)
113{
114 if (m_cursorNode != nullptr)
115 delete m_cursorNode;
116
117 m_cursorNode = m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color);
118 appendChildNode(m_cursorNode);
119}
120
121void QSGInternalTextNode::clearCursor()
122{
123 if (m_cursorNode)
124 removeChildNode(m_cursorNode);
125 delete m_cursorNode;
126 m_cursorNode = nullptr;
127}
128
129void QSGInternalTextNode::addDecorationNode(const QRectF &rect, const QColor &color)
130{
131 addRectangleNode(rect, color);
132}
133
134void QSGInternalTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
135{
136 appendChildNode(m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color));
137}
138
139void QSGInternalTextNode::addImage(const QRectF &rect, const QImage &image)
140{
141 QSGInternalImageNode *node = m_renderContext->sceneGraphContext()->createInternalImageNode(m_renderContext);
142 QSGTexture *texture = m_renderContext->createTexture(image);
143 texture->setFiltering(m_filtering);
144 m_textures.append(texture);
145 node->setTargetRect(rect);
146 node->setInnerTargetRect(rect);
147 node->setTexture(texture);
148 node->setFiltering(m_filtering);
149 appendChildNode(node);
150 node->update();
151}
152
153void QSGInternalTextNode::doAddTextDocument(QPointF position, QTextDocument *textDocument,
154 int selectionStart, int selectionEnd)
155{
156 QQuickTextNodeEngine engine;
157 engine.setTextColor(m_color);
158 engine.setSelectedTextColor(m_selectionTextColor);
159 engine.setSelectionColor(m_selectionColor);
160 engine.setAnchorColor(m_linkColor);
161 engine.setPosition(position);
162 engine.setDevicePixelRatio(m_devicePixelRatio);
163
164 QList<QTextFrame *> frames;
165 frames.append(textDocument->rootFrame());
166 while (!frames.isEmpty()) {
167 QTextFrame *textFrame = frames.takeFirst();
168 frames.append(textFrame->childFrames());
169
170 engine.addFrameDecorations(textDocument, textFrame);
171
172 if (textFrame->firstPosition() > textFrame->lastPosition()
173 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
174 const int pos = textFrame->firstPosition() - 1;
175 auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(textDocument->documentLayout());
176 QTextCharFormat format = a->formatAccessor(pos);
177 QRectF rect = a->frameBoundingRect(textFrame);
178
179 QTextBlock block = textFrame->firstCursorPosition().block();
180 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
181 engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument,
182 pos, textFrame->frameFormat().position());
183 } else {
184 QTextFrame::iterator it = textFrame->begin();
185
186 while (!it.atEnd()) {
187 Q_ASSERT(!engine.currentLine().isValid());
188
189 QTextBlock block = it.currentBlock();
190 engine.addTextBlock(textDocument, block, position, m_color, m_linkColor, selectionStart, selectionEnd,
191 (textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ?
192 m_viewport : QRectF()));
193 ++it;
194 }
195 }
196 }
197
198 engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
199}
200
201void QSGInternalTextNode::doAddTextLayout(QPointF position, QTextLayout *textLayout,
202 int selectionStart, int selectionEnd,
203 int lineStart, int lineCount)
204{
205 QQuickTextNodeEngine engine;
206 engine.setTextColor(m_color);
207 engine.setSelectedTextColor(m_selectionTextColor);
208 engine.setSelectionColor(m_selectionColor);
209 engine.setAnchorColor(m_linkColor);
210 engine.setPosition(position);
211 engine.setDevicePixelRatio(m_devicePixelRatio);
212
213#if QT_CONFIG(im)
214 int preeditLength = textLayout->preeditAreaText().size();
215 int preeditPosition = textLayout->preeditAreaPosition();
216#endif
217
218 QVarLengthArray<QTextLayout::FormatRange> colorChanges;
219 engine.mergeFormats(textLayout, &colorChanges);
220
221 lineCount = lineCount >= 0
222 ? qMin(lineStart + lineCount, textLayout->lineCount())
223 : textLayout->lineCount();
224
225 bool inViewport = false;
226 for (int i=lineStart; i<lineCount; ++i) {
227 QTextLine line = textLayout->lineAt(i);
228
229 int start = line.textStart();
230 int length = line.textLength();
231 int end = start + length;
232
233#if QT_CONFIG(im)
234 if (preeditPosition >= 0
235 && preeditPosition >= start
236 && preeditPosition < end) {
237 end += preeditLength;
238 }
239#endif
240 // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport.
241 if (m_viewport.isNull() || (line.y() + line.height() > m_viewport.top() && line.y() < m_viewport.bottom())) {
242 if (!inViewport && !m_viewport.isNull()) {
243 m_firstLineInViewport = i;
244 qCDebug(lcVP) << "first line in viewport" << i << "@" << line.y();
245 }
246 inViewport = true;
247 engine.setCurrentLine(line);
248 engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd);
249 } else if (inViewport) {
250 Q_ASSERT(!m_viewport.isNull());
251 m_firstLinePastViewport = i;
252 qCDebug(lcVP) << "first omitted line past bottom of viewport" << i << "@" << line.y();
253 break; // went past the bottom of the viewport, so we're done
254 }
255 }
256
257 engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor);
258}
259
260void QSGInternalTextNode::clear()
261{
262 while (firstChild() != nullptr)
263 delete firstChild();
264 m_cursorNode = nullptr;
265 qDeleteAll(m_textures);
266 m_textures.clear();
267}
268
269QT_END_NAMESPACE