Support font color with Text.StyledText in SceneGraph
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>
Wed, 31 Aug 2011 10:47:20 +0000 (12:47 +0200)
committerJiang Jiang <jiang.jiang@nokia.com>
Wed, 31 Aug 2011 16:13:38 +0000 (18:13 +0200)
When Text.StyledText is set as format, QML will use a QTextLayout
back-end with additional formats to render styled text, supporting
a few HTML tags. To support color changes using this format, we
need to process the additional formats in the text node. The code
that fetches glyph runs is now separated into its own function and
is called once per color-changing format if any such exist.

Task-number: QTBUG-21199
Change-Id: Ib9b0cbec8d23de2dfafae9f641a2e3f7819c6a7a
Reviewed-on: http://codereview.qt.nokia.com/3982
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Jiang Jiang <jiang.jiang@nokia.com>
src/declarative/items/qsgtextnode.cpp

index 1ae457b..a8a29ba 100644 (file)
@@ -281,6 +281,8 @@ namespace {
 
         void addSelectedGlyphs(const QGlyphRun &glyphRun);
         void addUnselectedGlyphs(const QGlyphRun &glyphRun);
+        void addGlyphsInRange(int rangeStart, int rangeEnd, const QColor &color,
+                              int selectionStart, int selectionEnd);
 
         void addToSceneGraph(QSGTextNode *parent,
                              QSGText::TextStyle style = QSGText::Normal,
@@ -568,6 +570,45 @@ namespace {
         m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize;
     }
 
+    void SelectionEngine::addGlyphsInRange(int rangeStart, int rangeLength,
+                                           const QColor &color,
+                                           int selectionStart, int selectionEnd)
+    {
+        QColor oldColor = m_textColor;
+        m_textColor = color;
+
+        QTextLine &line = m_currentLine;
+        int rangeEnd = rangeStart + rangeLength;
+        if (selectionStart > rangeEnd || selectionEnd < rangeStart) {
+            QList<QGlyphRun> glyphRuns = line.glyphRuns(rangeStart, rangeLength);
+            for (int j=0; j<glyphRuns.size(); ++j)
+                addUnselectedGlyphs(glyphRuns.at(j));
+        } else {
+            if (rangeStart < selectionStart) {
+                QList<QGlyphRun> glyphRuns = line.glyphRuns(rangeStart,
+                                                            qMin(selectionStart - rangeStart,
+                                                                 rangeLength));
+
+                for (int j=0; j<glyphRuns.size(); ++j)
+                    addUnselectedGlyphs(glyphRuns.at(j));
+            }
+
+            if (rangeEnd >= selectionStart && selectionStart >= rangeStart) {
+                QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionStart, selectionEnd - selectionStart + 1);
+
+                for (int j=0; j<glyphRuns.size(); ++j)
+                    addSelectedGlyphs(glyphRuns.at(j));
+            }
+
+            if (selectionEnd >= rangeStart && selectionEnd < rangeEnd) {
+                QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionEnd + 1, rangeEnd - selectionEnd);
+                for (int j=0; j<glyphRuns.size(); ++j)
+                    addUnselectedGlyphs(glyphRuns.at(j));
+            }
+        }
+        m_textColor = oldColor;
+    }
+
     void SelectionEngine::addToSceneGraph(QSGTextNode *parentNode,
                                           QSGText::TextStyle style,
                                           const QColor &styleColor)
@@ -761,39 +802,48 @@ void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout
     engine.setSelectionColor(selectionColor);
     engine.setPosition(position);
 
+    QList<QTextLayout::FormatRange> additionalFormats = textLayout->additionalFormats();
+    QVarLengthArray<QTextLayout::FormatRange> colorChanges;
+    for (int i=0; i<additionalFormats.size(); ++i) {
+        if (additionalFormats.at(i).format.hasProperty(QTextFormat::ForegroundBrush))
+            colorChanges.append(additionalFormats.at(i));
+    }
+
     for (int i=0; i<textLayout->lineCount(); ++i) {
         QTextLine line = textLayout->lineAt(i);
 
         engine.setCurrentLine(line);
 
-        int lineEnd = line.textStart() + line.textLength();
-        if (selectionStart > lineEnd || selectionEnd < line.textStart()) {
-            QList<QGlyphRun> glyphRuns = line.glyphRuns();
-            for (int j=0; j<glyphRuns.size(); ++j)
-                engine.addUnselectedGlyphs(glyphRuns.at(j));
-        } else {
-            if (line.textStart() < selectionStart) {
-                QList<QGlyphRun> glyphRuns = line.glyphRuns(line.textStart(),
-                                                            qMin(selectionStart - line.textStart(),
-                                                                 line.textLength()));
+        int currentPosition = line.textStart();
+        int remainingLength = line.textLength();
+        for (int j=0; j<colorChanges.size(); ++j) {
+            const QTextLayout::FormatRange &range = colorChanges.at(j);
+            if (range.start + range.length >= currentPosition
+                && range.start < currentPosition + remainingLength) {
 
-                for (int j=0; j<glyphRuns.size(); ++j)
-                    engine.addUnselectedGlyphs(glyphRuns.at(j));
-            }
+                if (range.start > currentPosition) {
+                    engine.addGlyphsInRange(currentPosition, range.start - currentPosition,
+                                            color, selectionStart, selectionEnd);
+                }
 
-            if (lineEnd >= selectionStart && selectionStart >= line.textStart()) {
-                QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionStart, selectionEnd - selectionStart + 1);
+                int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength);
+                QColor rangeColor = range.format.foreground().color();
 
-                for (int j=0; j<glyphRuns.size(); ++j)
-                    engine.addSelectedGlyphs(glyphRuns.at(j));
-            }
+                engine.addGlyphsInRange(range.start, rangeEnd - range.start, rangeColor,
+                                        selectionStart, selectionEnd);
 
-            if (selectionEnd >= line.textStart() && selectionEnd < lineEnd) {
-                QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionEnd + 1, lineEnd - selectionEnd);
-                for (int j=0; j<glyphRuns.size(); ++j)
-                    engine.addUnselectedGlyphs(glyphRuns.at(j));
+                currentPosition = range.start + range.length;
+                remainingLength = line.textStart() + line.textLength() - currentPosition;
+
+            } else if (range.start > currentPosition + remainingLength || remainingLength <= 0) {
+                break;
             }
         }
+
+        if (remainingLength > 0) {
+            engine.addGlyphsInRange(currentPosition, remainingLength, color,
+                                    selectionStart, selectionEnd);
+        }
     }
 
     engine.addToSceneGraph(this, style, styleColor);