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