diff options
Diffstat (limited to 'src/gui/text/qtextlayout.cpp')
-rw-r--r-- | src/gui/text/qtextlayout.cpp | 516 |
1 files changed, 275 insertions, 241 deletions
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 286722fc4c..f0c7dd24e5 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -265,6 +265,10 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const The text can then be rendered by calling the layout's draw() function: \snippet code/src_gui_text_qtextlayout.cpp 1 + It is also possible to draw each line individually, for instance to draw + the last line that fits into a widget elided: + \snippet code/src_gui_text_qtextlayout.cpp elided + For a given position in the text you can find a valid cursor position with isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition(). @@ -283,6 +287,7 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const /*! \enum QTextLayout::GlyphRunRetrievalFlag + \since 6.5 GlyphRunRetrievalFlag specifies flags passed to the glyphRuns() functions to determine which properties of the layout are returned in the QGlyphRun objects. Since each property @@ -736,7 +741,7 @@ int QTextLayout::leftCursorPosition(int oldPos) const return newPos; } -/*!/ +/*! Returns \c true if position \a pos is a valid cursor position. In a Unicode context some positions in the text are not valid @@ -1038,12 +1043,9 @@ QList<QGlyphRun> QTextLayout::glyphRuns(int from, for (int i=0; i<d->lines.size(); ++i) { if (d->lines.at(i).from > from + length) break; - else if (d->lines.at(i).from + d->lines[i].length >= from) { - QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags); - - for (int j = 0; j < glyphRuns.size(); j++) { - const QGlyphRun &glyphRun = glyphRuns.at(j); - + else if (d->lines.at(i).from + d->lines.at(i).length >= from) { + const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags); + for (const QGlyphRun &glyphRun : glyphRuns) { QRawFont rawFont = glyphRun.rawFont(); QFontEngine *fontEngine = rawFont.d->fontEngine; @@ -1102,7 +1104,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange> int firstLine = 0; int lastLine = d->lines.size(); for (int i = 0; i < d->lines.size(); ++i) { - QTextLine l(i, d); const QScriptLine &sl = d->lines.at(i); if (sl.y > clipe) { @@ -1303,7 +1304,6 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition QFixed base = sl.base(); QFixed descent = sl.descent; - QFixed cursorDescent = descent; bool rightToLeft = d->isRightToLeft(); const int realCursorPosition = cursorPosition; @@ -1323,7 +1323,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition int neighborItem = itm; if (neighborItem > 0 && si->position == realCursorPosition) --neighborItem; - else if (neighborItem < d->layoutData->items.count() - 1 && si->position + si->num_glyphs == realCursorPosition) + else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition) ++neighborItem; const bool onBoundary = neighborItem != itm && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel; @@ -1332,15 +1332,16 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition si = &d->layoutData->items[itm]; } } - if (si->ascent >= 0) - base = si->ascent; - if (si->descent == 0) - descent = si->descent; - else if (si->descent > 0 && si->descent < descent) - cursorDescent = si->descent; + // objects need some special treatment as they can have special alignment or be floating + if (si->analysis.flags != QScriptAnalysis::Object) { + if (si->ascent > 0) + base = si->ascent; + if (si->descent > 0) + descent = si->descent; + } rightToLeft = si->analysis.bidiLevel % 2; } - qreal y = position.y() + (sl.y + sl.base() + sl.descent - base - descent).toReal(); + qreal y = position.y() + (sl.y + sl.base() - base).toReal(); bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) && (p->transform().type() > QTransform::TxTranslate); if (toggleAntialiasing) @@ -1351,7 +1352,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition const QTransform &deviceTransform = p->deviceTransform(); const qreal xScale = deviceTransform.m11(); if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) { - p->fillRect(QRectF(x, y, qreal(width), (base + cursorDescent).toReal()), p->pen().brush()); + p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush()); } else { // Ensure consistently rendered cursor width under fractional scaling const QPen origPen = p->pen(); @@ -1359,7 +1360,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition pen.setCosmetic(true); const qreal center = x + qreal(width) / 2; p->setPen(pen); - p->drawLine(QPointF(center, y), QPointF(center, y + (base + cursorDescent).toReal())); + p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal()))); p->setPen(origPen); } p->setCompositionMode(origCompositionMode); @@ -1611,10 +1612,7 @@ void QTextLine::setLineWidth(qreal width) return; } - if (width > QFIXED_MAX) - width = QFIXED_MAX; - - line.width = QFixed::fromReal(width); + line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX))); if (line.length && line.textWidth <= line.width && line.from + line.length == eng->layoutData->string.size()) @@ -1654,7 +1652,7 @@ void QTextLine::setNumColumns(int numColumns) void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth) { QScriptLine &line = eng->lines[index]; - line.width = QFixed::fromReal(alignmentWidth); + line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX))); line.length = 0; line.textWidth = 0; layout_helper(numColumns); @@ -1670,23 +1668,18 @@ namespace { struct LineBreakHelper { - LineBreakHelper() - : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(nullptr), logClusters(nullptr), - manualWrap(false), whiteSpaceOrObject(true) - { - } - + LineBreakHelper() = default; QScriptLine tmpData; QScriptLine spaceData; QGlyphLayout glyphs; - int glyphCount; - int maxGlyphs; - int currentPosition; - glyph_t previousGlyph; - QFontEngine *previousGlyphFontEngine; + int glyphCount = 0; + int maxGlyphs = 0; + int currentPosition = 0; + glyph_t previousGlyph = 0; + QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine; QFixed minw; QFixed currentSoftHyphenWidth; @@ -1694,11 +1687,11 @@ namespace { QFixed rightBearing; QFixed minimumRightBearing; - QFontEngine *fontEngine; - const unsigned short *logClusters; + QExplicitlySharedDataPointer<QFontEngine> fontEngine; + const unsigned short *logClusters = nullptr; - bool manualWrap; - bool whiteSpaceOrObject; + bool manualWrap = false; + bool whiteSpaceOrObject = true; bool checkFullOtherwiseExtend(QScriptLine &line); @@ -1742,13 +1735,13 @@ namespace { { if (currentPosition <= 0) return; - calculateRightBearing(fontEngine, currentGlyph()); + calculateRightBearing(fontEngine.data(), currentGlyph()); } inline void calculateRightBearingForPreviousGlyph() { if (previousGlyph > 0) - calculateRightBearing(previousGlyphFontEngine, previousGlyph); + calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph); } static const QFixed RightBearingNotCalculated; @@ -1865,6 +1858,7 @@ void QTextLine::layout_helper(int maxGlyphs) lbh.logClusters = eng->layoutData->logClustersPtr; lbh.previousGlyph = 0; + bool manuallyWrapped = false; bool hasInlineObject = false; QFixed maxInlineObjectHeight = 0; @@ -1940,6 +1934,7 @@ void QTextLine::layout_helper(int maxGlyphs) lbh.calculateRightBearingForPreviousGlyph(); } line += lbh.tmpData; + manuallyWrapped = true; goto found; } else if (current.analysis.flags == QScriptAnalysis::Object) { lbh.whiteSpaceOrObject = true; @@ -1974,11 +1969,10 @@ void QTextLine::layout_helper(int maxGlyphs) addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount, current, lbh.logClusters, lbh.glyphs); } - - if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) { - goto found; - } } else { + if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) + goto found; + lbh.whiteSpaceOrObject = false; bool sb_or_ws = false; lbh.saveCurrentGlyph(); @@ -2161,7 +2155,15 @@ found: eng->maxWidth = qMax(eng->maxWidth, line.textWidth); } else { eng->minWidth = qMax(eng->minWidth, lbh.minw); - eng->maxWidth += line.textWidth + lbh.spaceData.textWidth; + if (qAddOverflow(eng->layoutData->currentMaxWidth, line.textWidth, &eng->layoutData->currentMaxWidth)) + eng->layoutData->currentMaxWidth = QFIXED_MAX; + if (!manuallyWrapped) { + if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth)) + eng->layoutData->currentMaxWidth = QFIXED_MAX; + } + eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth); + if (manuallyWrapped) + eng->layoutData->currentMaxWidth = 0; } line.textWidth += trailingSpace; @@ -2215,26 +2217,26 @@ int QTextLine::textStart() const int QTextLine::textLength() const { if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators - && eng->block.isValid() && index == eng->lines.count()-1) { + && eng->block.isValid() && index == eng->lines.size()-1) { return eng->lines.at(index).length - 1; } return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces; } -static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r) +static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r) { - QBrush c = chf.foreground(); - if (c.style() == Qt::NoBrush) { - p->setPen(defaultPen); - } - QBrush bg = chf.background(); if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool()) p->fillRect(r.toAlignedRect(), bg); - if (c.style() != Qt::NoBrush) { - p->setPen(QPen(c, 0)); - } +} +static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf) +{ + QBrush c = chf.foreground(); + if (c.style() == Qt::NoBrush) + p->setPen(defaultPen); + else + p->setPen(QPen(c, 0)); } #if !defined(QT_NO_RAWFONT) @@ -2484,14 +2486,18 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, // when we're breaking a RTL script item, since the expected position passed into // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run. if (relativeFrom != (iterator.itemStart - si.position) && !rtl) { - for (int i=itemGlyphsStart; i<glyphsStart; ++i) { - QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); - pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + for (int i = itemGlyphsStart; i < glyphsStart; ++i) { + if (!glyphLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); + pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + } } } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) { - for (int i=itemGlyphsEnd; i>glyphsEnd; --i) { - QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); - pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + for (int i = itemGlyphsEnd; i > glyphsEnd; --i) { + if (!glyphLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6); + pos.rx() += (glyphLayout.advances[i] + justification).toReal(); + } } } @@ -2528,22 +2534,28 @@ QList<QGlyphRun> QTextLine::glyphRuns(int from, if (start == 0 && startsInsideLigature) subFlags |= QGlyphRun::SplitLigature; - glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which), - eng->text, - subLayout, - pos, - subFlags, - retrievalFlags, - x, - width, - glyphsStart + start, - glyphsStart + end, - logClusters + relativeFrom, - relativeFrom + si.position, - relativeTo - relativeFrom + 1)); + { + QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which), + eng->text, + subLayout, + pos, + subFlags, + retrievalFlags, + x, + width, + glyphsStart + start, + glyphsStart + end, + logClusters + relativeFrom, + relativeFrom + si.position, + relativeTo - relativeFrom + 1); + if (!glyphRun.isEmpty()) + glyphRuns.append(glyphRun); + } for (int i = 0; i < subLayout.numGlyphs; ++i) { - QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6); - pos.rx() += (subLayout.advances[i] + justification).toReal(); + if (!subLayout.attributes[i].dontPrint) { + QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6); + pos.rx() += (subLayout.advances[i] + justification).toReal(); + } } if (rtl) @@ -2619,7 +2631,6 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, Q_ASSERT(!eng->useRawFont); #endif const QScriptLine &line = eng->lines[index]; - QPen pen = p->pen(); bool noText = (selection && selection->format.property(SuppressText).toBool()); @@ -2631,8 +2642,7 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, const qreal lineHeight = line.height().toReal(); QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(), lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' ')); - setPenAndDrawBackground(p, QPen(), selection->format, r); - p->setPen(pen); + drawBackground(p, selection->format, r); } return; } @@ -2645,7 +2655,7 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, else p->translate(origPos); - QTextLineItemIterator iterator(eng, index, pos, selection); + QFixed lineBase = line.base(); eng->clearDecorations(); eng->enableDelayDecorations(); @@ -2655,183 +2665,207 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, const QTextFormatCollection *formatCollection = eng->formatCollection(); bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors); - while (!iterator.atEnd()) { - QScriptItem &si = iterator.next(); - - if (selection && selection->start >= 0 && iterator.isOutsideSelection()) - continue; - - if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator - && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) - continue; - QFixed itemBaseLine = y; - QFont f = eng->font(si); - QTextCharFormat format; - if (formatCollection != nullptr) - format = formatCollection->defaultTextFormat(); + auto prepareFormat = [suppressColors, selection, this](QTextCharFormat &format, + QScriptItem *si) { + format.merge(eng->format(si)); - if (eng->hasFormats() || selection || formatCollection) { - format.merge(eng->format(&si)); + if (suppressColors) { + format.clearForeground(); + format.clearBackground(); + format.clearProperty(QTextFormat::TextUnderlineColor); + } + if (selection) + format.merge(selection->format); + }; - if (suppressColors) { - format.clearForeground(); - format.clearBackground(); - format.clearProperty(QTextFormat::TextUnderlineColor); - } - if (selection) - format.merge(selection->format); - - setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), - iterator.itemWidth.toReal(), line.height().toReal())); - - const qreal baseLineOffset = format.baselineOffset() / 100.0; - QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); - if (valign == QTextCharFormat::AlignSuperScript - || valign == QTextCharFormat::AlignSubScript - || !qFuzzyIsNull(baseLineOffset)) - { - QFontEngine *fe = f.d->engineForScript(si.analysis.script); - QFixed height = fe->ascent() + fe->descent(); - itemBaseLine -= height * QFixed::fromReal(baseLineOffset); - - if (valign == QTextCharFormat::AlignSubScript) - itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0); - else if (valign == QTextCharFormat::AlignSuperScript) - itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0); + { + QTextLineItemIterator iterator(eng, index, pos, selection); + while (!iterator.atEnd()) { + QScriptItem &si = iterator.next(); + + if (eng->hasFormats() || selection || formatCollection) { + QTextCharFormat format; + if (formatCollection != nullptr) + format = formatCollection->defaultTextFormat(); + prepareFormat(format, &si); + drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), + iterator.itemWidth.toReal(), line.height().toReal())); } } + } - if (si.analysis.flags >= QScriptAnalysis::TabOrObject) { + QPen pen = p->pen(); + { + QTextLineItemIterator iterator(eng, index, pos, selection); + while (!iterator.atEnd()) { + QScriptItem &si = iterator.next(); - if (eng->hasFormats()) { - p->save(); - if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) { - QFixed itemY = y - si.ascent; - switch (format.verticalAlignment()) { - case QTextCharFormat::AlignTop: - itemY = y - lineBase; - break; - case QTextCharFormat::AlignMiddle: - itemY = y - lineBase + (line.height() - si.height()) / 2; - break; - case QTextCharFormat::AlignBottom: - itemY = y - lineBase + line.height() - si.height(); - break; - default: - break; - } + if (selection && selection->start >= 0 && iterator.isOutsideSelection()) + continue; - QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); - - eng->docLayout()->drawInlineObject(p, itemRect, - QTextInlineObject(iterator.item, eng), - si.position + eng->block.position(), - format); - if (selection) { - QBrush bg = format.brushProperty(ObjectSelectionBrush); - if (bg.style() != Qt::NoBrush) { - QColor c = bg.color(); - c.setAlpha(128); - p->fillRect(itemRect, c); - } - } - } else { // si.isTab - QFont f = eng->font(si); - QTextItemInt gf(si, &f, format); - gf.chars = nullptr; - gf.num_chars = 0; - gf.width = iterator.itemWidth; - QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng); - if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) { - const QChar visualTab = QChar(QChar::VisualTabCharacter); - int w = QFontMetrics(f).horizontalAdvance(visualTab); - qreal x = iterator.itemWidth.toReal() - w; // Right-aligned - if (x < 0) - p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(), - iterator.itemWidth.toReal(), line.height().toReal()), - Qt::IntersectClip); - else - x /= 2; // Centered - p->setFont(f); - p->drawText(QPointF(iterator.x.toReal() + x, - y.toReal()), visualTab); - } + if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator + && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) + continue; + QFixed itemBaseLine = y; + QFont f = eng->font(si); + QTextCharFormat format; + if (formatCollection != nullptr) + format = formatCollection->defaultTextFormat(); + + if (eng->hasFormats() || selection || formatCollection) { + prepareFormat(format, &si); + setPen(p, pen, format); + + const qreal baseLineOffset = format.baselineOffset() / 100.0; + QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); + if (valign == QTextCharFormat::AlignSuperScript + || valign == QTextCharFormat::AlignSubScript + || !qFuzzyIsNull(baseLineOffset)) + { + QFontEngine *fe = f.d->engineForScript(si.analysis.script); + QFixed height = fe->ascent() + fe->descent(); + itemBaseLine -= height * QFixed::fromReal(baseLineOffset); + + if (valign == QTextCharFormat::AlignSubScript) + itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0); + else if (valign == QTextCharFormat::AlignSuperScript) + itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0); } - p->restore(); } - continue; - } + if (si.analysis.flags >= QScriptAnalysis::TabOrObject) { - unsigned short *logClusters = eng->logClusters(&si); - QGlyphLayout glyphs = eng->shapedGlyphs(&si); + if (eng->hasFormats()) { + p->save(); + if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) { + QFixed itemY = y - si.ascent; + switch (format.verticalAlignment()) { + case QTextCharFormat::AlignTop: + itemY = y - lineBase; + break; + case QTextCharFormat::AlignMiddle: + itemY = y - lineBase + (line.height() - si.height()) / 2; + break; + case QTextCharFormat::AlignBottom: + itemY = y - lineBase + line.height() - si.height(); + break; + default: + break; + } - QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart), - &f, eng->layoutData->string.unicode() + iterator.itemStart, - iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format); - gf.logClusters = logClusters + iterator.itemStart - si.position; - gf.width = iterator.itemWidth; - gf.justified = line.justified; - gf.initWithScriptItem(si); - - Q_ASSERT(gf.fontEngine); - - QPointF pos(iterator.x.toReal(), itemBaseLine.toReal()); - if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) { - QPainterPath path; - path.setFillRule(Qt::WindingFill); - - if (gf.glyphs.numGlyphs) - gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags); - if (gf.flags) { - const QFontEngine *fe = gf.fontEngine; - const qreal lw = fe->lineThickness().toReal(); - if (gf.flags & QTextItem::Underline) { - qreal offs = fe->underlinePosition().toReal(); - path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw); - } - if (gf.flags & QTextItem::Overline) { - qreal offs = fe->ascent().toReal() + 1; - path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); - } - if (gf.flags & QTextItem::StrikeOut) { - qreal offs = fe->ascent().toReal() / 3; - path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); + + eng->docLayout()->drawInlineObject(p, itemRect, + QTextInlineObject(iterator.item, eng), + si.position + eng->block.position(), + format); + if (selection) { + QBrush bg = format.brushProperty(ObjectSelectionBrush); + if (bg.style() != Qt::NoBrush) { + QColor c = bg.color(); + c.setAlpha(128); + p->fillRect(itemRect, c); + } + } + } else { // si.isTab + QFont f = eng->font(si); + QTextItemInt gf(si, &f, format); + gf.chars = nullptr; + gf.num_chars = 0; + gf.width = iterator.itemWidth; + QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng); + if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) { + const QChar visualTab = QChar(QChar::VisualTabCharacter); + int w = QFontMetrics(f).horizontalAdvance(visualTab); + qreal x = iterator.itemWidth.toReal() - w; // Right-aligned + if (x < 0) + p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(), + iterator.itemWidth.toReal(), line.height().toReal()), + Qt::IntersectClip); + else + x /= 2; // Centered + p->setFont(f); + p->drawText(QPointF(iterator.x.toReal() + x, + y.toReal()), visualTab); + } + + } + p->restore(); } + + continue; } - p->save(); - p->setRenderHint(QPainter::Antialiasing); - //Currently QPen with a Qt::NoPen style still returns a default - //QBrush which != Qt::NoBrush so we need this specialcase to reset it - if (p->pen().style() == Qt::NoPen) - p->setBrush(Qt::NoBrush); - else - p->setBrush(p->pen().brush()); + unsigned short *logClusters = eng->logClusters(&si); + QGlyphLayout glyphs = eng->shapedGlyphs(&si); - p->setPen(format.textOutline()); - p->drawPath(path); - p->restore(); - } else { - if (noText) - gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be - QPainterPrivate::get(p)->drawTextItem(pos, gf, eng); - } + QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart), + &f, eng->layoutData->string.unicode() + iterator.itemStart, + iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format); + gf.logClusters = logClusters + iterator.itemStart - si.position; + gf.width = iterator.itemWidth; + gf.justified = line.justified; + gf.initWithScriptItem(si); + + Q_ASSERT(gf.fontEngine); + + QPointF pos(iterator.x.toReal(), itemBaseLine.toReal()); + if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) { + QPainterPath path; + path.setFillRule(Qt::WindingFill); + + if (gf.glyphs.numGlyphs) + gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags); + if (gf.flags) { + const QFontEngine *fe = gf.fontEngine; + const qreal lw = fe->lineThickness().toReal(); + if (gf.flags & QTextItem::Underline) { + qreal offs = fe->underlinePosition().toReal(); + path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw); + } + if (gf.flags & QTextItem::Overline) { + qreal offs = fe->ascent().toReal() + 1; + path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + } + if (gf.flags & QTextItem::StrikeOut) { + qreal offs = fe->ascent().toReal() / 3; + path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + } + } - if ((si.analysis.flags == QScriptAnalysis::Space - || si.analysis.flags == QScriptAnalysis::Nbsp) - && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) { - QBrush c = format.foreground(); - if (c.style() != Qt::NoBrush) - p->setPen(c.color()); - const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0'; - QFont oldFont = p->font(); - p->setFont(eng->font(si)); - p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace); - p->setPen(pen); - p->setFont(oldFont); + p->save(); + p->setRenderHint(QPainter::Antialiasing); + //Currently QPen with a Qt::NoPen style still returns a default + //QBrush which != Qt::NoBrush so we need this specialcase to reset it + if (p->pen().style() == Qt::NoPen) + p->setBrush(Qt::NoBrush); + else + p->setBrush(p->pen().brush()); + + p->setPen(format.textOutline()); + p->drawPath(path); + p->restore(); + } else { + if (noText) + gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be + QPainterPrivate::get(p)->drawTextItem(pos, gf, eng); + } + + if ((si.analysis.flags == QScriptAnalysis::Space + || si.analysis.flags == QScriptAnalysis::Nbsp) + && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) { + QBrush c = format.foreground(); + if (c.style() != Qt::NoBrush) + p->setPen(c.color()); + const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0'; + QFont oldFont = p->font(); + p->setFont(eng->font(si)); + p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace); + p->setPen(pen); + p->setFont(oldFont); + } } } eng->drawDecorations(p); @@ -2900,7 +2934,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const int neighborItem = itm; if (neighborItem > 0 && scriptItem->position == pos) --neighborItem; - else if (neighborItem < eng->layoutData->items.count() - 1 && scriptItem->position + scriptItem->num_glyphs == pos) + else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos) ++neighborItem; const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel; // If we are, prioritise the neighbor item that has the same direction as the engine @@ -3217,7 +3251,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const // character between lines is a space and we want // to position the cursor to the left of that // character. - if (index < eng->lines.count() - 1) + if (index < eng->lines.size() - 1) pos = qMin(eng->previousLogicalPosition(pos), pos); return pos; |