/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qsgtextnode_p.h" #include "qsgsimplerectnode.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE /*! Creates an empty QSGTextNode */ QSGTextNode::QSGTextNode(QSGContext *context) : m_context(context), m_cursorNode(0) { #if defined(QML_RUNTIME_TESTING) description = QLatin1String("text"); #endif } QSGTextNode::~QSGTextNode() { } #if 0 void QSGTextNode::setColor(const QColor &color) { if (m_usePixmapCache) { setUpdateFlag(UpdateNodes); } else { for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { if (childNode->subType() == GlyphNodeSubType) { QSGGlyphNode *glyphNode = static_cast(childNode); if (glyphNode->color() == m_color) glyphNode->setColor(color); } else if (childNode->subType() == SolidRectNodeSubType) { QSGSimpleRectNode *solidRectNode = static_cast(childNode); if (solidRectNode->color() == m_color) solidRectNode->setColor(color); } } } m_color = color; } void QSGTextNode::setStyleColor(const QColor &styleColor) { if (m_textStyle != QSGTextNode::NormalTextStyle) { if (m_usePixmapCache) { setUpdateFlag(UpdateNodes); } else { for (QSGNode *childNode = firstChild(); childNode; childNode = childNode->nextSibling()) { if (childNode->subType() == GlyphNodeSubType) { QSGGlyphNode *glyphNode = static_cast(childNode); if (glyphNode->color() == m_styleColor) glyphNode->setColor(styleColor); } else if (childNode->subType() == SolidRectNodeSubType) { QSGSimpleRectNode *solidRectNode = static_cast(childNode); if (solidRectNode->color() == m_styleColor) solidRectNode->setColor(styleColor); } } } } m_styleColor = styleColor; } #endif QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color, QSGText::TextStyle style, const QColor &styleColor, QSGNode *parentNode) { QSGGlyphNode *node = m_context->createGlyphNode(); node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs); node->setStyle(style); node->setStyleColor(styleColor); node->setColor(color); node->update(); if (parentNode == 0) parentNode = this; parentNode->appendChildNode(node); return node; } void QSGTextNode::setCursor(const QRectF &rect, const QColor &color) { if (m_cursorNode != 0) delete m_cursorNode; m_cursorNode = new QSGSimpleRectNode(rect, color); appendChildNode(m_cursorNode); } namespace { struct BinaryTreeNode { enum SelectionState { Unselected, Selected }; BinaryTreeNode() : selectionState(Unselected) , clipNode(0) , decorations(QSGTextNode::NoDecoration) , leftChildIndex(-1) , rightChildIndex(-1) { } BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, const QSGTextNode::Decorations &decs, const QColor &c, const QPointF &pos) : glyphRun(g) , boundingRect(brect) , selectionState(selState) , clipNode(0) , decorations(decs) , color(c) , position(pos) , leftChildIndex(-1) , rightChildIndex(-1) { } QGlyphRun glyphRun; QRectF boundingRect; SelectionState selectionState; QSGClipNode *clipNode; QSGTextNode::Decorations decorations; QColor color; QPointF position; int leftChildIndex; int rightChildIndex; static void insert(QVarLengthArray *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState, const QColor &textColor, const QPointF &position) { int newIndex = binaryTree->size(); QRectF searchRect = glyphRun.boundingRect(); if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height())) return; QSGTextNode::Decorations decorations = QSGTextNode::NoDecoration; decorations |= (glyphRun.underline() ? QSGTextNode::Underline : QSGTextNode::NoDecoration); decorations |= (glyphRun.overline() ? QSGTextNode::Overline : QSGTextNode::NoDecoration); decorations |= (glyphRun.strikeOut() ? QSGTextNode::StrikeOut : QSGTextNode::NoDecoration); binaryTree->append(BinaryTreeNode(glyphRun, selectionState, searchRect, decorations, textColor, position)); if (newIndex == 0) return; int searchIndex = 0; forever { BinaryTreeNode *node = binaryTree->data() + searchIndex; if (searchRect.left() < node->boundingRect.left()) { if (node->leftChildIndex < 0) { node->leftChildIndex = newIndex; break; } else { searchIndex = node->leftChildIndex; } } else { if (node->rightChildIndex < 0) { node->rightChildIndex = newIndex; break; } else { searchIndex = node->rightChildIndex; } } } } static void inOrder(const QVarLengthArray &binaryTree, QVarLengthArray *sortedIndexes, int currentIndex = 0) { Q_ASSERT(currentIndex < binaryTree.size()); const BinaryTreeNode *node = binaryTree.data() + currentIndex; if (node->leftChildIndex >= 0) inOrder(binaryTree, sortedIndexes, node->leftChildIndex); sortedIndexes->append(currentIndex); if (node->rightChildIndex >= 0) inOrder(binaryTree, sortedIndexes, node->rightChildIndex); } }; // Engine that takes glyph runs as input, and produces a set of glyph nodes, clip nodes, // and rectangle nodes to represent the text, decorations and selection. Will try to minimize // number of nodes, and join decorations in neighbouring items class SelectionEngine { public: SelectionEngine() : m_hasSelection(false) {} QTextLine currentLine() const { return m_currentLine; } void setCurrentLine(const QTextLine ¤tLine) { if (m_currentLine.isValid()) processCurrentLine(); m_currentLine = currentLine; } void addSelectedGlyphs(const QGlyphRun &glyphRun); void addUnselectedGlyphs(const QGlyphRun &glyphRun); void addToSceneGraph(QSGTextNode *parent, QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor()); void setSelectionColor(const QColor &selectionColor) { m_selectionColor = selectionColor; } void setSelectedTextColor(const QColor &selectedTextColor) { m_selectedTextColor = selectedTextColor; } void setTextColor(const QColor &textColor) { m_textColor = textColor; } void setPosition(const QPointF &position) { m_position = position; } private: struct TextDecoration { TextDecoration() : selectionState(BinaryTreeNode::Unselected) {} TextDecoration(const BinaryTreeNode::SelectionState &s, const QRectF &r, const QColor &c) : selectionState(s) , rect(r) , color(c) { } BinaryTreeNode::SelectionState selectionState; QRectF rect; QColor color; }; void processCurrentLine(); void addTextDecorations(const QVarLengthArray &textDecorations, qreal offset, qreal thickness); QColor m_selectionColor; QColor m_textColor; QColor m_selectedTextColor; QPointF m_position; QTextLine m_currentLine; bool m_hasSelection; QList m_selectionRects; QVarLengthArray m_currentLineTree; QList m_lines; QVector m_processedNodes; }; void SelectionEngine::addTextDecorations(const QVarLengthArray &textDecorations, qreal offset, qreal thickness) { for (int i=0; i sortedIndexes; // Indexes in tree sorted by x position BinaryTreeNode::inOrder(m_currentLineTree, &sortedIndexes); Q_ASSERT(sortedIndexes.size() == m_currentLineTree.size()); BinaryTreeNode::SelectionState currentSelectionState = BinaryTreeNode::Unselected; QRectF currentRect; QSGTextNode::Decorations currentDecorations = QSGTextNode::NoDecoration; qreal underlineOffset = 0.0; qreal underlineThickness = 0.0; qreal overlineOffset = 0.0; qreal overlineThickness = 0.0; qreal strikeOutOffset = 0.0; qreal strikeOutThickness = 0.0; QRectF decorationRect = currentRect; QColor lastColor; QVarLengthArray pendingUnderlines; QVarLengthArray pendingOverlines; QVarLengthArray pendingStrikeOuts; if (!sortedIndexes.isEmpty()) { QSGClipNode *currentClipNode = m_hasSelection ? new QSGClipNode : 0; bool currentClipNodeUsed = false; for (int i=0; i<=sortedIndexes.size(); ++i) { BinaryTreeNode *node = 0; if (i < sortedIndexes.size()) { int sortedIndex = sortedIndexes.at(i); Q_ASSERT(sortedIndex < m_currentLineTree.size()); node = m_currentLineTree.data() + sortedIndex; } if (i == 0) currentSelectionState = node->selectionState; // Update decorations if (currentDecorations != QSGTextNode::NoDecoration) { decorationRect.setY(m_position.y() + m_currentLine.y()); decorationRect.setHeight(m_currentLine.height()); if (node != 0) decorationRect.setRight(node->boundingRect.left()); TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor); if (currentDecorations & QSGTextNode::Underline) pendingUnderlines.append(textDecoration); if (currentDecorations & QSGTextNode::Overline) pendingOverlines.append(textDecoration); if (currentDecorations & QSGTextNode::StrikeOut) pendingStrikeOuts.append(textDecoration); } // If we've reached an unselected node from a selected node, we add the // selection rect to the graph, and we add decoration every time the // selection state changes, because that means the text color changes if (node == 0 || node->selectionState != currentSelectionState) { if (node != 0) currentRect.setRight(node->boundingRect.left()); currentRect.setY(m_position.y() + m_currentLine.y()); currentRect.setHeight(m_currentLine.height()); // Draw selection all the way up to the left edge of the unselected item if (currentSelectionState == BinaryTreeNode::Selected) m_selectionRects.append(currentRect); if (currentClipNode != 0) { if (!currentClipNodeUsed) { delete currentClipNode; } else { currentClipNode->setIsRectangular(true); currentClipNode->setClipRect(currentRect); } } if (node != 0 && m_hasSelection) currentClipNode = new QSGClipNode; else currentClipNode = 0; currentClipNodeUsed = false; if (node != 0) { currentSelectionState = node->selectionState; currentRect = node->boundingRect; // Make sure currentRect is valid, otherwise the unite won't work if (currentRect.isNull()) currentRect.setSize(QSizeF(1, 1)); } } else { if (currentRect.isNull()) currentRect = node->boundingRect; else currentRect = currentRect.united(node->boundingRect); } if (node != 0) { node->clipNode = currentClipNode; currentClipNodeUsed = true; decorationRect = node->boundingRect; // If previous item(s) had underline and current does not, then we add the // pending lines to the lists and likewise for overlines and strikeouts if (!pendingUnderlines.isEmpty() && !(node->decorations & QSGTextNode::Underline)) { addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); pendingUnderlines.clear(); underlineOffset = 0.0; underlineThickness = 0.0; } // ### Add pending when overlineOffset/thickness changes to minimize number of // nodes if (!pendingOverlines.isEmpty()) { addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); pendingOverlines.clear(); overlineOffset = 0.0; overlineThickness = 0.0; } // ### Add pending when overlineOffset/thickness changes to minimize number of // nodes if (!pendingStrikeOuts.isEmpty()) { addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); pendingStrikeOuts.clear(); strikeOutOffset = 0.0; strikeOutThickness = 0.0; } // Merge current values with previous. Prefer greatest thickness QRawFont rawFont = node->glyphRun.rawFont(); if (node->decorations & QSGTextNode::Underline) { if (rawFont.lineThickness() > underlineThickness) { underlineThickness = rawFont.lineThickness(); underlineOffset = rawFont.underlinePosition(); } } if (node->decorations & QSGTextNode::Overline) { overlineOffset = -rawFont.ascent(); overlineThickness = rawFont.lineThickness(); } if (node->decorations & QSGTextNode::StrikeOut) { strikeOutThickness = rawFont.lineThickness(); strikeOutOffset = rawFont.ascent() / -3.0; } currentDecorations = node->decorations; lastColor = node->color; m_processedNodes.append(*node); } } // If there are pending decorations, we need to add them if (!pendingUnderlines.isEmpty()) addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); if (!pendingOverlines.isEmpty()) addTextDecorations(pendingOverlines, overlineOffset, overlineThickness); if (!pendingStrikeOuts.isEmpty()) addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness); } m_currentLineTree.clear(); m_currentLine = QTextLine(); m_hasSelection = false; } void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun) { BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected, m_textColor, m_position); } void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun) { int currentSize = m_currentLineTree.size(); BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected, m_textColor, m_position); m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize; } void SelectionEngine::addToSceneGraph(QSGTextNode *parentNode, QSGText::TextStyle style, const QColor &styleColor) { if (m_currentLine.isValid()) processCurrentLine(); // First, prepend all selection rectangles to the tree for (int i=0; iappendChildNode(new QSGSimpleRectNode(rect, m_selectionColor)); } // Then, go through all the nodes for all lines and combine all QGlyphRuns with a common // font, selection state and clip node. typedef QPair > > KeyType; QHash map; for (int i=0; iglyphRun; QRawFont rawFont = glyphRun.rawFont(); QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont); QFontEngine *fontEngine = rawFontD->fontEngine; KeyType key(qMakePair(fontEngine, qMakePair(node->clipNode, qMakePair(node->color.rgba(), int(node->selectionState))))); BinaryTreeNode *otherNode = map.value(key, 0); if (otherNode != 0) { QGlyphRun &otherGlyphRun = otherNode->glyphRun; QVector otherGlyphIndexes = otherGlyphRun.glyphIndexes(); QVector otherGlyphPositions = otherGlyphRun.positions(); otherGlyphIndexes += glyphRun.glyphIndexes(); QVector glyphPositions = glyphRun.positions(); for (int j=0; jposition - otherNode->position); } otherGlyphRun.setGlyphIndexes(otherGlyphIndexes); otherGlyphRun.setPositions(otherGlyphPositions); } else { map.insert(key, node); } } // ...and add clip nodes and glyphs to tree. QHash::const_iterator it = map.constBegin(); while (it != map.constEnd()) { BinaryTreeNode *node = it.value(); QSGClipNode *clipNode = node->clipNode; if (clipNode != 0 && clipNode->parent() == 0 ) parentNode->appendChildNode(clipNode); QColor color = node->selectionState == BinaryTreeNode::Selected ? m_selectedTextColor : node->color; parentNode->addGlyphs(node->position, node->glyphRun, color, style, styleColor, clipNode); ++it; } // Finally, add decorations for each node to the tree. for (int i=0; iappendChildNode(new QSGSimpleRectNode(textDecoration.rect, color)); } } } void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, const QColor &textColor, QSGText::TextStyle style, const QColor &styleColor, const QColor &selectionColor, const QColor &selectedTextColor, int selectionStart, int selectionEnd) { QTextFrame *textFrame = textDocument->rootFrame(); QPointF position = textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft(); SelectionEngine engine; engine.setTextColor(textColor); engine.setSelectedTextColor(selectedTextColor); engine.setSelectionColor(selectionColor); bool hasSelection = selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd; QTextFrame::iterator it = textFrame->begin(); while (!it.atEnd()) { Q_ASSERT(!engine.currentLine().isValid()); QTextBlock block = it.currentBlock(); QTextBlock::iterator blockIterator = block.begin(); while (!blockIterator.atEnd()) { QTextFragment fragment = blockIterator.fragment(); if (fragment.text().isEmpty()) continue; QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft(); engine.setPosition(position + blockPosition); QTextCharFormat charFormat = fragment.charFormat(); if (!textColor.isValid()) engine.setTextColor(charFormat.foreground().color()); int fragmentEnd = fragment.position() + fragment.length(); int textPos = fragment.position(); while (textPos < fragmentEnd) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); Q_ASSERT(line.textLength() > 0); if (!engine.currentLine().isValid() || line.lineNumber() != engine.currentLine().lineNumber()) engine.setCurrentLine(line); int lineEnd = line.textStart() + block.position() + line.textLength(); int len = qMin(lineEnd - textPos, fragmentEnd - textPos); Q_ASSERT(len > 0); int currentStepEnd = textPos + len; if (!hasSelection || selectionStart > currentStepEnd || selectionEnd < textPos) { QList glyphRuns = fragment.glyphRuns(blockRelativePosition, len); for (int j=0; j glyphRuns = fragment.glyphRuns(blockRelativePosition, unselectedPartLength); for (int j=0; j textPos) { int currentSelectionStart = qMax(selectionStart, textPos); int currentSelectionLength = qMin(selectionEnd - currentSelectionStart, currentStepEnd - currentSelectionStart); int selectionRelativeBlockPosition = currentSelectionStart - block.position(); QList glyphRuns = fragment.glyphRuns(selectionRelativeBlockPosition, currentSelectionLength); for (int j=0; j= textPos && selectionEnd < currentStepEnd) { QList glyphRuns = fragment.glyphRuns(selectionEnd - block.position(), currentStepEnd - selectionEnd); for (int j=0; jlineCount(); ++i) { QTextLine line = textLayout->lineAt(i); engine.setCurrentLine(line); int lineEnd = line.textStart() + line.textLength(); if (selectionStart > lineEnd || selectionEnd < line.textStart()) { QList glyphRuns = line.glyphRuns(); for (int j=0; j glyphRuns = line.glyphRuns(line.textStart(), qMin(selectionStart - line.textStart(), line.textLength())); for (int j=0; j= selectionStart && selectionStart >= line.textStart()) { QList glyphRuns = line.glyphRuns(selectionStart, selectionEnd - selectionStart + 1); for (int j=0; j= line.textStart() && selectionEnd < lineEnd) { QList glyphRuns = line.glyphRuns(selectionEnd + 1, lineEnd - selectionEnd); for (int j=0; j supportedTags; if (supportedTags.isEmpty()) { supportedTags.insert(QLatin1String("i")); supportedTags.insert(QLatin1String("b")); supportedTags.insert(QLatin1String("u")); supportedTags.insert(QLatin1String("div")); supportedTags.insert(QLatin1String("big")); supportedTags.insert(QLatin1String("blockquote")); supportedTags.insert(QLatin1String("body")); supportedTags.insert(QLatin1String("br")); supportedTags.insert(QLatin1String("center")); supportedTags.insert(QLatin1String("cite")); supportedTags.insert(QLatin1String("code")); supportedTags.insert(QLatin1String("tt")); supportedTags.insert(QLatin1String("dd")); supportedTags.insert(QLatin1String("dfn")); supportedTags.insert(QLatin1String("em")); supportedTags.insert(QLatin1String("font")); supportedTags.insert(QLatin1String("h1")); supportedTags.insert(QLatin1String("h2")); supportedTags.insert(QLatin1String("h3")); supportedTags.insert(QLatin1String("h4")); supportedTags.insert(QLatin1String("h5")); supportedTags.insert(QLatin1String("h6")); supportedTags.insert(QLatin1String("head")); supportedTags.insert(QLatin1String("html")); supportedTags.insert(QLatin1String("meta")); supportedTags.insert(QLatin1String("nobr")); supportedTags.insert(QLatin1String("p")); supportedTags.insert(QLatin1String("pre")); supportedTags.insert(QLatin1String("qt")); supportedTags.insert(QLatin1String("s")); supportedTags.insert(QLatin1String("samp")); supportedTags.insert(QLatin1String("small")); supportedTags.insert(QLatin1String("span")); supportedTags.insert(QLatin1String("strong")); supportedTags.insert(QLatin1String("sub")); supportedTags.insert(QLatin1String("sup")); supportedTags.insert(QLatin1String("title")); supportedTags.insert(QLatin1String("var")); supportedTags.insert(QLatin1String("style")); } static QSet supportedCssProperties; if (supportedCssProperties.isEmpty()) { supportedCssProperties.insert(QCss::Color); supportedCssProperties.insert(QCss::Float); supportedCssProperties.insert(QCss::Font); supportedCssProperties.insert(QCss::FontFamily); supportedCssProperties.insert(QCss::FontSize); supportedCssProperties.insert(QCss::FontStyle); supportedCssProperties.insert(QCss::FontWeight); supportedCssProperties.insert(QCss::Margin); supportedCssProperties.insert(QCss::MarginBottom); supportedCssProperties.insert(QCss::MarginLeft); supportedCssProperties.insert(QCss::MarginRight); supportedCssProperties.insert(QCss::MarginTop); supportedCssProperties.insert(QCss::TextDecoration); supportedCssProperties.insert(QCss::TextIndent); supportedCssProperties.insert(QCss::TextUnderlineStyle); supportedCssProperties.insert(QCss::VerticalAlignment); supportedCssProperties.insert(QCss::Whitespace); supportedCssProperties.insert(QCss::Padding); supportedCssProperties.insert(QCss::PaddingLeft); supportedCssProperties.insert(QCss::PaddingRight); supportedCssProperties.insert(QCss::PaddingTop); supportedCssProperties.insert(QCss::PaddingBottom); supportedCssProperties.insert(QCss::PageBreakBefore); supportedCssProperties.insert(QCss::PageBreakAfter); supportedCssProperties.insert(QCss::Width); supportedCssProperties.insert(QCss::Height); supportedCssProperties.insert(QCss::MinimumWidth); supportedCssProperties.insert(QCss::MinimumHeight); supportedCssProperties.insert(QCss::MaximumWidth); supportedCssProperties.insert(QCss::MaximumHeight); supportedCssProperties.insert(QCss::Left); supportedCssProperties.insert(QCss::Right); supportedCssProperties.insert(QCss::Top); supportedCssProperties.insert(QCss::Bottom); supportedCssProperties.insert(QCss::Position); supportedCssProperties.insert(QCss::TextAlignment); supportedCssProperties.insert(QCss::FontVariant); } QXmlStreamReader reader(doc->toHtml("utf-8")); while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement()) { if (!supportedTags.contains(reader.name().toString().toLower())) return true; QXmlStreamAttributes attributes = reader.attributes(); if (attributes.hasAttribute(QLatin1String("bgcolor"))) return true; if (attributes.hasAttribute(QLatin1String("style"))) { QCss::StyleSheet styleSheet; QCss::Parser(attributes.value(QLatin1String("style")).toString()).parse(&styleSheet); QVector decls; for (int i=0; i styleRules = styleSheet.styleRules + styleSheet.idIndex.values().toVector() + styleSheet.nameIndex.values().toVector(); for (int i=0; ipropertyId)) return true; } } } } return reader.hasError(); } void QSGTextNode::deleteContent() { while (firstChild() != 0) delete firstChild(); m_cursorNode = 0; } #if 0 void QSGTextNode::updateNodes() { return; deleteContent(); if (m_text.isEmpty()) return; if (m_usePixmapCache) { // ### gunnar: port properly // QPixmap pixmap = generatedPixmap(); // if (pixmap.isNull()) // return; // QSGImageNode *pixmapNode = m_context->createImageNode(); // pixmapNode->setRect(pixmap.rect()); // pixmapNode->setSourceRect(pixmap.rect()); // pixmapNode->setOpacity(m_opacity); // pixmapNode->setClampToEdge(true); // pixmapNode->setLinearFiltering(m_linearFiltering); // appendChildNode(pixmapNode); } else { if (m_text.isEmpty()) return; // Implement styling by drawing text several times at slight shifts. shiftForStyle // contains the sequence of shifted positions at which to draw the text. All except // the last will be drawn with styleColor. QList shiftForStyle; switch (m_textStyle) { case OutlineTextStyle: // ### Should be made faster by implementing outline material shiftForStyle << QPointF(-1, 0); shiftForStyle << QPointF(0, -1); shiftForStyle << QPointF(1, 0); shiftForStyle << QPointF(0, 1); break; case SunkenTextStyle: shiftForStyle << QPointF(0, -1); break; case RaisedTextStyle: shiftForStyle << QPointF(0, 1); break; default: break; } shiftForStyle << QPointF(0, 0); // Regular position while (!shiftForStyle.isEmpty()) { QPointF shift = shiftForStyle.takeFirst(); // Use styleColor for all but last shift if (m_richText) { QColor overrideColor = shiftForStyle.isEmpty() ? QColor() : m_styleColor; QTextFrame *textFrame = m_textDocument->rootFrame(); QPointF p = m_textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft(); QTextFrame::iterator it = textFrame->begin(); while (!it.atEnd()) { addTextBlock(shift + p, it.currentBlock(), overrideColor); ++it; } } else { addTextLayout(shift, m_textLayout, shiftForStyle.isEmpty() ? m_color : m_styleColor); } } } } #endif QT_END_NAMESPACE