diff options
Diffstat (limited to 'src/quick/items/qsginternaltextnode.cpp')
-rw-r--r-- | src/quick/items/qsginternaltextnode.cpp | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/quick/items/qsginternaltextnode.cpp b/src/quick/items/qsginternaltextnode.cpp new file mode 100644 index 0000000000..6a64c0b9e4 --- /dev/null +++ b/src/quick/items/qsginternaltextnode.cpp @@ -0,0 +1,265 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qsginternaltextnode_p.h" + +#include "qquicktextnodeengine_p.h" + +#include <private/qsgadaptationlayer_p.h> +#include <private/qsgdistancefieldglyphnode_p.h> +#include <private/qquickclipnode_p.h> +#include <private/qquickitem_p.h> +#include <private/qquicktextdocument_p.h> + +#include <QtCore/qpoint.h> +#include <qtextdocument.h> +#include <qtextlayout.h> +#include <qabstracttextdocumentlayout.h> +#include <private/qquickstyledtext_p.h> +#include <private/qquicktext_p_p.h> +#include <private/qfont_p.h> +#include <private/qfontengine_p.h> + +#include <private/qtextdocumentlayout_p.h> +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcVP) + +/*! + Creates an empty QSGInternalTextNode +*/ +QSGInternalTextNode::QSGInternalTextNode(QSGRenderContext *renderContext) + : m_renderContext(renderContext) +{ +#ifdef QSG_RUNTIME_DESCRIPTION + qsgnode_set_description(this, QLatin1String("text")); +#endif + + static_assert(int(QSGTextNode::Normal) == int(QQuickText::Normal)); + static_assert(int(QSGTextNode::Outline) == int(QQuickText::Outline)); + static_assert(int(QSGTextNode::Raised) == int(QQuickText::Raised)); + static_assert(int(QSGTextNode::Sunken) == int(QQuickText::Sunken)); + + static_assert(int(QSGTextNode::QtRendering) == int(QQuickText::QtRendering)); + static_assert(int(QSGTextNode::NativeRendering) == int(QQuickText::NativeRendering)); + static_assert(int(QSGTextNode::CurveRendering) == int(QQuickText::CurveRendering)); +} + +QSGInternalTextNode::~QSGInternalTextNode() +{ + qDeleteAll(m_textures); +} + +QSGGlyphNode *QSGInternalTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, + QQuickText::TextStyle style, const QColor &styleColor, + QSGNode *parentNode) +{ + QRawFont font = glyphs.rawFont(); + + QSGTextNode::RenderType preferredRenderType = m_renderType; + if (m_renderType != NativeRendering) { + if (const QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine) + if (fe->hasUnreliableGlyphOutline() || !fe->isSmoothlyScalable) + preferredRenderType = QSGTextNode::NativeRendering; + } + + QSGGlyphNode *node = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext, + preferredRenderType, + m_renderTypeQuality); + node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); + node->setStyle(style); + node->setStyleColor(styleColor); + node->setColor(color); + node->update(); + + /* We flag the geometry as static, but we never call markVertexDataDirty + or markIndexDataDirty on them. This is because all text nodes are + discarded when a change occurs. If we start appending/removing from + existing geometry, then we also need to start marking the geometry as + dirty. + */ + node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); + node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); + + if (parentNode == nullptr) + parentNode = this; + parentNode->appendChildNode(node); + + if (style == QQuickText::Outline && color.alpha() > 0 && styleColor != color) { + QSGGlyphNode *fillNode = m_renderContext->sceneGraphContext()->createGlyphNode(m_renderContext, + preferredRenderType, + m_renderTypeQuality); + fillNode->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); + fillNode->setStyle(QQuickText::Normal); + fillNode->setPreferredAntialiasingMode(QSGGlyphNode::GrayAntialiasing); + fillNode->setColor(color); + fillNode->update(); + + fillNode->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern); + fillNode->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern); + + parentNode->appendChildNode(fillNode); + fillNode->setRenderOrder(node->renderOrder() + 1); + } + + return node; +} + +void QSGInternalTextNode::setCursor(const QRectF &rect, const QColor &color) +{ + if (m_cursorNode != nullptr) + delete m_cursorNode; + + m_cursorNode = m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color); + appendChildNode(m_cursorNode); +} + +void QSGInternalTextNode::clearCursor() +{ + if (m_cursorNode) + removeChildNode(m_cursorNode); + delete m_cursorNode; + m_cursorNode = nullptr; +} + +void QSGInternalTextNode::addDecorationNode(const QRectF &rect, const QColor &color) +{ + addRectangleNode(rect, color); +} + +void QSGInternalTextNode::addRectangleNode(const QRectF &rect, const QColor &color) +{ + appendChildNode(m_renderContext->sceneGraphContext()->createInternalRectangleNode(rect, color)); +} + +void QSGInternalTextNode::addImage(const QRectF &rect, const QImage &image) +{ + QSGInternalImageNode *node = m_renderContext->sceneGraphContext()->createInternalImageNode(m_renderContext); + QSGTexture *texture = m_renderContext->createTexture(image); + texture->setFiltering(m_filtering); + m_textures.append(texture); + node->setTargetRect(rect); + node->setInnerTargetRect(rect); + node->setTexture(texture); + node->setFiltering(m_filtering); + appendChildNode(node); + node->update(); +} + +void QSGInternalTextNode::doAddTextDocument(QPointF position, QTextDocument *textDocument, + int selectionStart, int selectionEnd) +{ + QQuickTextNodeEngine engine; + engine.setTextColor(m_color); + engine.setSelectedTextColor(m_selectionTextColor); + engine.setSelectionColor(m_selectionColor); + engine.setAnchorColor(m_linkColor); + engine.setPosition(position); + + QList<QTextFrame *> frames; + frames.append(textDocument->rootFrame()); + while (!frames.isEmpty()) { + QTextFrame *textFrame = frames.takeFirst(); + frames.append(textFrame->childFrames()); + + engine.addFrameDecorations(textDocument, textFrame); + + if (textFrame->firstPosition() > textFrame->lastPosition() + && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) { + const int pos = textFrame->firstPosition() - 1; + auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(textDocument->documentLayout()); + QTextCharFormat format = a->formatAccessor(pos); + QRectF rect = a->frameBoundingRect(textFrame); + + QTextBlock block = textFrame->firstCursorPosition().block(); + engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); + engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument, + pos, textFrame->frameFormat().position()); + } else { + QTextFrame::iterator it = textFrame->begin(); + + while (!it.atEnd()) { + Q_ASSERT(!engine.currentLine().isValid()); + + QTextBlock block = it.currentBlock(); + engine.addTextBlock(textDocument, block, position, m_color, m_linkColor, selectionStart, selectionEnd, + (textDocument->characterCount() > QQuickTextPrivate::largeTextSizeThreshold ? + m_viewport : QRectF())); + ++it; + } + } + } + + engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor); +} + +void QSGInternalTextNode::doAddTextLayout(QPointF position, QTextLayout *textLayout, + int selectionStart, int selectionEnd, + int lineStart, int lineCount) +{ + QQuickTextNodeEngine engine; + engine.setTextColor(m_color); + engine.setSelectedTextColor(m_selectionTextColor); + engine.setSelectionColor(m_selectionColor); + engine.setAnchorColor(m_linkColor); + engine.setPosition(position); + +#if QT_CONFIG(im) + int preeditLength = textLayout->preeditAreaText().size(); + int preeditPosition = textLayout->preeditAreaPosition(); +#endif + + QVarLengthArray<QTextLayout::FormatRange> colorChanges; + engine.mergeFormats(textLayout, &colorChanges); + + lineCount = lineCount >= 0 + ? qMin(lineStart + lineCount, textLayout->lineCount()) + : textLayout->lineCount(); + + bool inViewport = false; + for (int i=lineStart; i<lineCount; ++i) { + QTextLine line = textLayout->lineAt(i); + + int start = line.textStart(); + int length = line.textLength(); + int end = start + length; + +#if QT_CONFIG(im) + if (preeditPosition >= 0 + && preeditPosition >= start + && preeditPosition < end) { + end += preeditLength; + } +#endif + // If there's a lot of text, insert only the range of lines that can possibly be visible within the viewport. + if (m_viewport.isNull() || (line.y() + line.height() > m_viewport.top() && line.y() < m_viewport.bottom())) { + if (!inViewport && !m_viewport.isNull()) { + m_firstLineInViewport = i; + qCDebug(lcVP) << "first line in viewport" << i << "@" << line.y(); + } + inViewport = true; + engine.setCurrentLine(line); + engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); + } else if (inViewport) { + Q_ASSERT(!m_viewport.isNull()); + m_firstLinePastViewport = i; + qCDebug(lcVP) << "first omitted line past bottom of viewport" << i << "@" << line.y(); + break; // went past the bottom of the viewport, so we're done + } + } + + engine.addToSceneGraph(this, QQuickText::TextStyle(m_textStyle), m_styleColor); +} + +void QSGInternalTextNode::clear() +{ + while (firstChild() != nullptr) + delete firstChild(); + m_cursorNode = nullptr; + qDeleteAll(m_textures); + m_textures.clear(); +} + +QT_END_NAMESPACE |