📄 qtextlayout.cpp
字号:
\sa maximumWidth()*/qreal QTextLayout::minimumWidth() const{ return d->minWidth.toReal();}/*! The maximum width the layout could expand to; this is essentially the width of the entire text. \warning This function only returns a valid value after the layout has been done. \sa minimumWidth()*/qreal QTextLayout::maximumWidth() const{ return d->maxWidth.toReal();}struct QTextLineItemIterator{ QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(), const QTextLayout::FormatRange *_selection = 0); inline bool atEnd() const { return logicalItem >= nItems - 1; } QScriptItem &next(); bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const; inline bool isOutsideSelection() const { QFixed tmp1, tmp2; return !getSelectionBounds(&tmp1, &tmp2); } QTextEngine *eng; QFixed x; QFixed pos_x; const QScriptLine &line; QScriptItem *si; int lineEnd; int firstItem; int lastItem; int nItems; int logicalItem; int item; int itemLength; int glyphsStart; int glyphsEnd; int itemStart; int itemEnd; QFixed itemWidth; QVarLengthArray<int> visualOrder; QVarLengthArray<uchar> levels; const QTextLayout::FormatRange *selection;};QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos, const QTextLayout::FormatRange *_selection) : eng(_eng), line(eng->lines[lineNum]), si(0), lineEnd(line.from + line.length), firstItem(eng->findItem(line.from)), lastItem(eng->findItem(lineEnd - 1)), nItems(lastItem - firstItem + 1), logicalItem(-1), item(-1), visualOrder(nItems), levels(nItems), selection(_selection){ pos_x = x = QFixed::fromReal(pos.x()); x += line.x; x += alignLine(eng, line); for (int i = 0; i < nItems; ++i) levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());}QScriptItem &QTextLineItemIterator::next(){ x += itemWidth; ++logicalItem; item = visualOrder[logicalItem] + firstItem; itemLength = eng->length(item); si = &eng->layoutData->items[item]; if (!si->num_glyphs) eng->shape(item); if (si->isObject) { itemWidth = si->width; return *si; } else if (si->isTab) { itemWidth = eng->nextTab(si, x - pos_x) - (x - pos_x); return *si; } unsigned short *logClusters = eng->logClusters(si); QGlyphLayout *glyphs = eng->glyphs(si); itemStart = qMax(line.from, si->position); glyphsStart = logClusters[itemStart - si->position]; if (lineEnd < si->position + itemLength) { itemEnd = lineEnd; glyphsEnd = logClusters[itemEnd-si->position]; } else { itemEnd = si->position + itemLength; glyphsEnd = si->num_glyphs; } // show soft-hyphen at line-break if (si->position + itemLength >= lineEnd && eng->layoutData->string.at(lineEnd - 1) == 0x00ad) glyphs[glyphsEnd - 1].attributes.dontPrint = false; itemWidth = 0; for (int g = glyphsStart; g < glyphsEnd; ++g) itemWidth += (glyphs[g].advance.x + QFixed::fromFixed(glyphs[g].space_18d6)) * !glyphs[g].attributes.dontPrint; return *si;}bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const{ *selectionX = *selectionWidth = 0; if (!selection) return false; if (si->isObject || si->isTab) { if (si->position >= selection->start + selection->length || si->position + itemLength <= selection->start) return false; *selectionX = x; *selectionWidth = itemWidth; } else { unsigned short *logClusters = eng->logClusters(si); QGlyphLayout *glyphs = eng->glyphs(si); int from = qMax(itemStart, selection->start) - si->position; int to = qMin(itemEnd, selection->start + selection->length) - si->position; if (from >= to) return false; int start_glyph = logClusters[from]; int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to]; QFixed soff; QFixed swidth; if (si->analysis.bidiLevel %2) { for (int g = glyphsEnd - 1; g >= end_glyph; --g) soff += (glyphs[g].advance.x + QFixed::fromFixed(glyphs[g].space_18d6)) * !glyphs[g].attributes.dontPrint; for (int g = end_glyph - 1; g >= start_glyph; --g) swidth += (glyphs[g].advance.x + QFixed::fromFixed(glyphs[g].space_18d6)) * !glyphs[g].attributes.dontPrint; } else { for (int g = glyphsStart; g < start_glyph; ++g) soff += (glyphs[g].advance.x + QFixed::fromFixed(glyphs[g].space_18d6)) * !glyphs[g].attributes.dontPrint; for (int g = start_glyph; g < end_glyph; ++g) swidth += (glyphs[g].advance.x + QFixed::fromFixed(glyphs[g].space_18d6)) * !glyphs[g].attributes.dontPrint; } *selectionX = x + soff; *selectionWidth = swidth; } return true;}static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection, QPainterPath *region){ const QScriptLine &line = eng->lines[lineNumber]; if (!line.length) { if (selection && selection->start <= line.from && selection->start + selection->length > line.from) { const qreal lineHeight = line.height().toReal(); QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(), lineHeight / 2, lineHeight); region->addRect(r); } return; } QTextLineItemIterator iterator(eng, lineNumber, pos, selection); const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; const qreal lineHeight = line.height().toReal(); const qreal selectionY = (y - line.ascent).toReal(); QFixed lastSelectionX = iterator.x; QFixed lastSelectionWidth; while (!iterator.atEnd()) { iterator.next(); QFixed selectionX, selectionWidth; if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) { if (selectionX == lastSelectionX + lastSelectionWidth) { lastSelectionWidth += selectionWidth; continue; } if (lastSelectionWidth > 0) region->addRect(QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight)); lastSelectionX = selectionX; lastSelectionWidth = selectionWidth; } } if (lastSelectionWidth > 0) region->addRect(QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));}/*! Draws the whole layout on the painter \a p at the position specified by \a pos. The rendered layout includes the given \a selections and is clipped within the rectangle specified by \a clip.*/void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const{ if (d->lines.isEmpty()) return; if (!d->layoutData) d->itemize(); QPointF position = pos + d->position; QFixed clipy = (INT_MIN/256); QFixed clipe = (INT_MAX/256); if (clip.isValid()) { clipy = QFixed::fromReal(clip.y() - position.y()); clipe = clipy + QFixed::fromReal(clip.height()); } 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[i]; if (sl.y > clipe) { lastLine = i; break; } if ((sl.y + sl.height()) < clipy) { firstLine = i; continue; } l.draw(p, position); } for (int i = 0; i < selections.size(); ++i) { FormatRange selection = selections.at(i); const QBrush bg = selection.format.background(); QPainterPath region; region.setFillRule(Qt::WindingFill); for (int line = firstLine; line < lastLine; ++line) { const QScriptLine &sl = d->lines[line]; if (selection.format.boolProperty(QTextFormat::FullWidthSelection) && selection.start >= sl.from && (selection.start < sl.from + sl.length || !sl.length) ) { QRectF selectionRect; selectionRect.setLeft(0); selectionRect.setWidth(INT_MAX); selectionRect.setY(position.y() + sl.y.toReal()); selectionRect.setHeight(sl.height().toReal()); region.addRect(selectionRect); } addSelectedRegionsToPath(d, line, position, &selection, ®ion); } { const QPen oldPen = p->pen(); const QBrush oldBrush = p->brush(); p->setPen(selection.format.penProperty(QTextFormat::OutlinePen)); p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush)); p->drawPath(region); p->setPen(oldPen); p->setBrush(oldBrush); } p->save(); p->setClipPath(region, Qt::IntersectClip); selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush)); // don't just clear the property, set an empty brush that overrides a potential // background brush specified in the text selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush()); selection.format.clearProperty(QTextFormat::OutlinePen); for (int line = firstLine; line < lastLine; ++line) { QTextLine l(line, d); l.draw(p, position, &selection); } p->restore(); } if (!d->cacheGlyphs) d->freeMemory();}/*! \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const \overload Draws a text cursor with the current pen at the given \a position using the \a painter specified. The corresponding position within the text is specified by \a cursorPosition.*/void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const{ drawCursor(p, pos, cursorPosition, 1);}/*! \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const Draws a text cursor with the current pen and the specified \a width at the given \a position using the \a painter specified. The corresponding position within the text is specified by \a cursorPosition.*/void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const{ if (d->lines.isEmpty()) return; if (!d->layoutData) d->itemize(); QPointF position = pos + d->position; QFixed pos_x = QFixed::fromReal(position.x()); QFixed pos_y = QFixed::fromReal(position.y()); cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length()); int line = 0; if (cursorPosition == d->layoutData->string.length()) { line = d->lines.size() - 1; } else { // ### binary search for (line = 0; line < d->lines.size(); line++) { const QScriptLine &sl = d->lines[line]; if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition) break; } } if (line >= d->lines.size()) return; QTextLine l(line, d); const QScriptLine &sl = d->lines[line]; const qreal x = position.x() + l.cursorToX(cursorPosition); int itm = d->findItem(cursorPosition - 1); QFixed ascent = sl.ascent; QFixed descent = sl.descent; bool rightToLeft = (d->option.textDirection() == Qt::RightToLeft); if (itm >= 0) { const QScriptItem &si = d->layoutData->items.at(itm); if (si.ascent > 0) ascent = si.ascent; if (si.descent > 0) descent = si.descent; rightToLeft = si.analysis.bidiLevel % 2; } qreal y = position.y() + (sl.y + sl.ascent - ascent).toReal(); p->fillRect(QRectF(x, y, qreal(width), (ascent + descent).toReal()), p->pen().brush()); if (d->layoutData->hasBidi) { const int arrow_extent = 4; int sign = rightToLeft ? -1 : 1; p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2)); p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2)); } return;}/*! \class QTextLine \brief The QTextLine class represents a line of text inside a QTextLayout. \ingroup text A text line is usually created by QTextLayout::createLine(). After being created, the line can be filled using the setLineWidth() or setNumColumns() functions. A line has a number of attributes including the rectangle it occupies, rect(), its coordinates, x() and y(), its textLength(), width() and naturalTextWidth(), and its ascent() and decent() relative to the text. The position of the cursor in terms of the line is available from cursorToX() and its inverse from xToCursor(). A line can be moved with setPosition().*//*! \enum QTextLine::Edge \value Leading
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -