From 931f631ed8cd9af00a697c641540df442a2377df Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 14 Sep 2011 12:13:28 +0200 Subject: Fix pre-edit text in in QSGTextEdit We need to include the preedit text when getting the glyphs for the fragment, and we need to support background formats. Note: This assumes that the pre-edit text will not span several fragments, which seems like a reasonable assumption. Task-number: QTBUG-21261 Change-Id: I7dd28f1221f931893ba1c51cc9e8b9771b2e46c3 Reviewed-on: http://codereview.qt-project.org/4906 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/declarative/items/qsgtextnode.cpp | 264 ++++++++++++++++++++++------------ src/declarative/items/qsgtextnode_p.h | 3 +- 2 files changed, 178 insertions(+), 89 deletions(-) diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp index a8a29bad24..8e31903c66 100644 --- a/src/declarative/items/qsgtextnode.cpp +++ b/src/declarative/items/qsgtextnode.cpp @@ -176,7 +176,8 @@ namespace { } BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect, - const QSGTextNode::Decorations &decs, const QColor &c, + const QSGTextNode::Decorations &decs, + const QColor &c, const QColor &bc, const QPointF &pos) : glyphRun(g) , boundingRect(brect) @@ -184,6 +185,7 @@ namespace { , clipNode(0) , decorations(decs) , color(c) + , backgroundColor(bc) , position(pos) , leftChildIndex(-1) , rightChildIndex(-1) @@ -196,6 +198,7 @@ namespace { QSGClipNode *clipNode; QSGTextNode::Decorations decorations; QColor color; + QColor backgroundColor; QPointF position; int leftChildIndex; @@ -205,6 +208,7 @@ namespace { const QGlyphRun &glyphRun, SelectionState selectionState, const QColor &textColor, + const QColor &backgroundColor, const QPointF &position) { int newIndex = binaryTree->size(); @@ -217,9 +221,10 @@ namespace { decorations |= (glyphRun.underline() ? QSGTextNode::Underline : QSGTextNode::NoDecoration); decorations |= (glyphRun.overline() ? QSGTextNode::Overline : QSGTextNode::NoDecoration); decorations |= (glyphRun.strikeOut() ? QSGTextNode::StrikeOut : QSGTextNode::NoDecoration); + decorations |= (backgroundColor.isValid() ? QSGTextNode::Background : QSGTextNode::NoDecoration); binaryTree->append(BinaryTreeNode(glyphRun, selectionState, searchRect, decorations, - textColor, position)); + textColor, backgroundColor, position)); if (newIndex == 0) return; @@ -281,8 +286,12 @@ namespace { void addSelectedGlyphs(const QGlyphRun &glyphRun); void addUnselectedGlyphs(const QGlyphRun &glyphRun); - void addGlyphsInRange(int rangeStart, int rangeEnd, const QColor &color, + void addGlyphsInRange(int rangeStart, int rangeEnd, + const QColor &color, const QColor &backgroundColor, int selectionStart, int selectionEnd); + void addGlyphsForRanges(const QVarLengthArray &ranges, + int start, int end, + int selectionStart, int selectionEnd); void addToSceneGraph(QSGTextNode *parent, QSGText::TextStyle style = QSGText::Normal, @@ -332,6 +341,7 @@ namespace { QColor m_selectionColor; QColor m_textColor; + QColor m_backgroundColor; QColor m_selectedTextColor; QPointF m_position; @@ -394,10 +404,12 @@ namespace { QRectF decorationRect = currentRect; QColor lastColor; + QColor lastBackgroundColor; QVarLengthArray pendingUnderlines; QVarLengthArray pendingOverlines; QVarLengthArray pendingStrikeOuts; + QVarLengthArray pendingBackgrounds; if (!sortedIndexes.isEmpty()) { QSGClipNode *currentClipNode = m_hasSelection ? new QSGClipNode : 0; bool currentClipNodeUsed = false; @@ -430,6 +442,12 @@ namespace { if (currentDecorations & QSGTextNode::StrikeOut) pendingStrikeOuts.append(textDecoration); + + if (currentDecorations & QSGTextNode::Background) { + pendingBackgrounds.append(TextDecoration(BinaryTreeNode::Unselected, + decorationRect, + lastBackgroundColor)); + } } // If we've reached an unselected node from a selected node, we add the @@ -481,6 +499,13 @@ namespace { decorationRect = node->boundingRect; + if (!pendingBackgrounds.isEmpty()) { + addTextDecorations(pendingBackgrounds, -m_currentLine.ascent(), + m_currentLine.height()); + + pendingBackgrounds.clear(); + } + // 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() @@ -536,11 +561,17 @@ namespace { currentDecorations = node->decorations; lastColor = node->color; + lastBackgroundColor = node->backgroundColor; m_processedNodes.append(*node); } } // If there are pending decorations, we need to add them + if (!pendingBackgrounds.isEmpty()) { + addTextDecorations(pendingBackgrounds, -m_currentLine.ascent(), + m_currentLine.height()); + } + if (!pendingUnderlines.isEmpty()) addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness); @@ -559,27 +590,83 @@ namespace { void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun) { BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected, - m_textColor, m_position); + m_textColor, m_backgroundColor, 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_textColor, m_backgroundColor, m_position); m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize; } + void SelectionEngine::addGlyphsForRanges(const QVarLengthArray &ranges, + int start, int end, + int selectionStart, int selectionEnd) + { + int currentPosition = start; + int remainingLength = end - start; + for (int j=0; j= currentPosition + && range.start < currentPosition + remainingLength) { + + if (range.start > currentPosition) { + addGlyphsInRange(currentPosition, range.start - currentPosition, + QColor(), QColor(), selectionStart, selectionEnd); + } + + int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength); + QColor rangeColor = range.format.hasProperty(QTextFormat::ForegroundBrush) + ? range.format.foreground().color() + : QColor(); + QColor rangeBackgroundColor = range.format.hasProperty(QTextFormat::BackgroundBrush) + ? range.format.background().color() + : QColor(); + + addGlyphsInRange(range.start, rangeEnd - range.start, + rangeColor, rangeBackgroundColor, + selectionStart, selectionEnd); + + currentPosition = range.start + range.length; + remainingLength = end - currentPosition; + + } else if (range.start > currentPosition + remainingLength || remainingLength <= 0) { + break; + } + } + + if (remainingLength > 0) { + addGlyphsInRange(currentPosition, remainingLength, QColor(), QColor(), + selectionStart, selectionEnd); + } + + } + void SelectionEngine::addGlyphsInRange(int rangeStart, int rangeLength, - const QColor &color, + const QColor &color, const QColor &backgroundColor, int selectionStart, int selectionEnd) { - QColor oldColor = m_textColor; - m_textColor = color; + QColor oldColor; + if (color.isValid()) { + oldColor = m_textColor; + m_textColor = color; + } + + QColor oldBackgroundColor = m_backgroundColor; + if (backgroundColor.isValid()) { + oldBackgroundColor = m_backgroundColor; + m_backgroundColor = backgroundColor; + } + + bool hasSelection = selectionStart >= 0 + && selectionEnd >= 0 + && selectionStart != selectionEnd; QTextLine &line = m_currentLine; int rangeEnd = rangeStart + rangeLength; - if (selectionStart > rangeEnd || selectionEnd < rangeStart) { + if (!hasSelection || (selectionStart > rangeEnd || selectionEnd < rangeStart)) { QList glyphRuns = line.glyphRuns(rangeStart, rangeLength); for (int j=0; jappendChildNode(new QSGSimpleRectNode(rect, m_selectionColor)); } + // Finally, add decorations for each node to the tree. + for (int i=0; iappendChildNode(new QSGSimpleRectNode(textDecoration.rect, color)); + } + // 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; @@ -680,17 +783,6 @@ namespace { ++it; } - - // Finally, add decorations for each node to the tree. - for (int i=0; iappendChildNode(new QSGSimpleRectNode(textDecoration.rect, color)); - } } } @@ -708,15 +800,64 @@ void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, 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(); + int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0; + int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1; + + QTextLayout *textLayout = block.layout(); + QList additionalFormats; + if (textLayout != 0) + additionalFormats = textLayout->additionalFormats(); + QVarLengthArray colorChanges; + for (int i=0; istart + lastFormat->length) { + QTextLayout::FormatRange *mergedRange = 0; + + int length = additionalFormat.length; + if (additionalFormat.start > lastFormat->start) { + lastFormat->length = additionalFormat.start - lastFormat->start; + length -= lastFormat->length; + + colorChanges.append(QTextLayout::FormatRange()); + mergedRange = colorChanges.data() + colorChanges.size() - 1; + lastFormat = colorChanges.data() + colorChanges.size() - 2; + } else { + mergedRange = lastFormat; + } + + mergedRange->format = lastFormat->format; + mergedRange->format.merge(additionalFormat.format); + mergedRange->start = additionalFormat.start; + + int end = qMin(additionalFormat.start + additionalFormat.length, + lastFormat->start + lastFormat->length); + + mergedRange->length = end - mergedRange->start; + length -= mergedRange->length; + + additionalFormat.start = end; + additionalFormat.length = length; + } + } + + if (additionalFormat.length > 0) + colorChanges.append(additionalFormat); + } + } QTextBlock::iterator blockIterator = block.begin(); + int textPos = block.position(); while (!blockIterator.atEnd()) { QTextFragment fragment = blockIterator.fragment(); if (fragment.text().isEmpty()) @@ -729,8 +870,13 @@ void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, if (!textColor.isValid()) engine.setTextColor(charFormat.foreground().color()); - int fragmentEnd = fragment.position() + fragment.length(); - int textPos = fragment.position(); + int fragmentEnd = textPos + fragment.length(); + if (preeditPosition >= 0 + && preeditPosition >= textPos + && preeditPosition < fragmentEnd) { + fragmentEnd += preeditLength; + } + while (textPos < fragmentEnd) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); @@ -745,38 +891,8 @@ void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument, 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; jlineAt(i); engine.setCurrentLine(line); - - int currentPosition = line.textStart(); - int remainingLength = line.textLength(); - for (int j=0; j= currentPosition - && range.start < currentPosition + remainingLength) { - - if (range.start > currentPosition) { - engine.addGlyphsInRange(currentPosition, range.start - currentPosition, - color, selectionStart, selectionEnd); - } - - int rangeEnd = qMin(range.start + range.length, currentPosition + remainingLength); - QColor rangeColor = range.format.foreground().color(); - - engine.addGlyphsInRange(range.start, rangeEnd - range.start, rangeColor, - selectionStart, selectionEnd); - - 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.addGlyphsForRanges(colorChanges, + line.textStart(), line.textStart() + line.textLength(), + selectionStart, selectionEnd); } engine.addToSceneGraph(this, style, styleColor); diff --git a/src/declarative/items/qsgtextnode_p.h b/src/declarative/items/qsgtextnode_p.h index 31a1fbe558..6d68fd1df6 100644 --- a/src/declarative/items/qsgtextnode_p.h +++ b/src/declarative/items/qsgtextnode_p.h @@ -67,7 +67,8 @@ public: NoDecoration = 0x0, Underline = 0x1, Overline = 0x2, - StrikeOut = 0x4 + StrikeOut = 0x4, + Background = 0x8 }; Q_DECLARE_FLAGS(Decorations, Decoration) -- cgit v1.2.3