📄 qtextlayout.cpp
字号:
// You create a text layout with a string of text. Once you laid// it out, it contains a number of QTextLines. from() returns the position// inside the text string where this line starts. If you e.g. has a// text of "This is a string", laid out into two lines (the second// starting at the word 'a'), layout.lineAt(0).from() == 0 and// layout.lineAt(1).from() == 8./*! Returns the start of the line from the beginning of the string passed to the QTextLayout.*/int QTextLine::textStart() const{ return eng->lines[i].from;}/*! Returns the length of the text in the line. \sa naturalTextWidth()*/int QTextLine::textLength() const{ return eng->lines[i].length;}static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng, int start, int glyph_start){ int ge = glyph_start + gf.num_glyphs; int gs = glyph_start; int end = start + gf.num_chars; unsigned short *logClusters = eng->logClusters(&si); QGlyphLayout *glyphs = eng->glyphs(&si); QFixed orig_width = gf.width; int *ul = eng->underlinePositions; if (ul) while (*ul != -1 && *ul < start) ++ul; bool rtl = si.analysis.bidiLevel % 2; if (rtl) x += si.width; do { int gtmp = ge; int stmp = end; if (ul && *ul != -1 && *ul < end) { stmp = *ul; gtmp = logClusters[*ul-si.position]; } gf.num_glyphs = gtmp - gs; gf.glyphs = glyphs + gs; gf.num_chars = stmp - start; gf.chars = eng->layoutData->string.unicode() + start; QFixed w = 0; while (gs < gtmp) { w += (glyphs[gs].advance.x + QFixed::fromFixed(glyphs[gs].space_18d6)) * !glyphs[gs].attributes.dontPrint; ++gs; } start = stmp; gf.width = w; if (rtl) x -= w; if (gf.num_chars) p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf); if (!rtl) x += w; if (ul && *ul != -1 && *ul < end) { // draw underline gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position]; ++stmp; gf.num_glyphs = gtmp - gs; gf.glyphs = glyphs + gs; gf.num_chars = stmp - start; gf.chars = eng->layoutData->string.unicode() + start; gf.logClusters = logClusters + start - si.position; w = 0; while (gs < gtmp) { w += (glyphs[gs].advance.x + QFixed::fromFixed(glyphs[gs].space_18d6)) * !glyphs[gs].attributes.dontPrint; ++gs; } ++start; gf.width = w; gf.underlineStyle = QTextCharFormat::SingleUnderline; if (rtl) x -= w; p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf); if (!rtl) x += w; gf.underlineStyle = QTextCharFormat::NoUnderline; ++gf.chars; ++ul; } } while (gs < ge); gf.width = orig_width;}static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, 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) p->fillRect(r, bg); if (c.style() != Qt::NoBrush) p->setPen(QPen(c, 0));}/*! \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const Draws a line on the given \a painter at the specified \a position. The \a selection is reserved for internal use.*/void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const{ const QScriptLine &line = eng->lines[i]; if (!line.length) { if (selection && selection->start <= line.from && selection->start + selection->length > line.from) { p->save(); const qreal lineHeight = line.height().toReal(); QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(), lineHeight / 2, lineHeight); setPenAndDrawBackground(p, QPen(), selection->format, r); p->restore(); } return; } QPen pen = p->pen(); QTextLineItemIterator iterator(eng, i, pos, selection); const QFixed y = QFixed::fromReal(pos.y()) + line.y + line.ascent; while (!iterator.atEnd()) { QScriptItem &si = iterator.next(); if (si.isObject || si.isTab) { if (eng->layoutData->string.at(si.position) == QChar::LineSeparator) continue; if (eng->hasFormats() && (!selection || (si.position < selection->start + selection->length && si.position + iterator.itemLength > selection->start))) { p->save(); QTextCharFormat format = eng->format(&si); if (selection) format.merge(selection->format); setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(), iterator.itemWidth.toReal(), line.height().toReal())); if (si.isObject && eng->block.docHandle()) { QFixed itemY = y - si.ascent; if (format.verticalAlignment() == QTextCharFormat::AlignTop) { itemY = y - line.ascent; } 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.num_glyphs = 0; gf.chars = 0; gf.num_chars = 0; gf.width = iterator.itemWidth; p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf); } p->restore(); } continue; } if (selection && iterator.isOutsideSelection()) continue; unsigned short *logClusters = eng->logClusters(&si); QGlyphLayout *glyphs = eng->glyphs(&si); QFixed itemBaseLine = y; QFont f = eng->font(si); QTextCharFormat chf; if (eng->hasFormats() || selection) { chf = eng->format(&si); if (selection) chf.merge(selection->format); setPenAndDrawBackground(p, pen, chf, QRectF(iterator.x.toReal(), (y - line.ascent).toReal(), iterator.itemWidth.toReal(), line.height().toReal())); QTextCharFormat::VerticalAlignment valign = chf.verticalAlignment(); if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) { QFontEngine *fe = f.d->engineForScript(si.analysis.script); QFixed height = fe->ascent() + fe->descent(); if (valign == QTextCharFormat::AlignSubScript) itemBaseLine += height / 6; else if (valign == QTextCharFormat::AlignSuperScript) itemBaseLine -= height / 2; } } QTextItemInt gf(si, &f, chf); gf.num_glyphs = iterator.glyphsEnd - iterator.glyphsStart; gf.glyphs = glyphs + iterator.glyphsStart; gf.chars = eng->layoutData->string.unicode() + iterator.itemStart; gf.logClusters = logClusters + iterator.itemStart - si.position; gf.num_chars = iterator.itemEnd - iterator.itemStart; gf.width = iterator.itemWidth; gf.justified = line.justified; Q_ASSERT(gf.fontEngine); if (eng->underlinePositions) { // can't have selections in this case drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart); } else { QPointF pos(iterator.x.toReal(), itemBaseLine.toReal()); if (chf.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) { QPainterPath path; path.setFillRule(Qt::WindingFill); if (gf.num_glyphs) gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, gf.num_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); } } 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(chf.textOutline()); p->drawPath(path); p->restore(); } else { p->drawTextItem(pos, gf); } } } if (eng->hasFormats()) p->setPen(pen);}/*! \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const \overload*//*! Converts the cursor position \a cursorPos to the corresponding x position inside the line, taking account of the \a edge. If \a cursorPos is not a valid cursor position, the nearest valid cursor position will be used instead, and cpos will be modified to point to this valid cursor position. \sa xToCursor()*/qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const{ if (!eng->layoutData) eng->itemize(); const QScriptLine &line = eng->lines[i]; QFixed x = line.x; x += alignLine(eng, line); if (!i && !eng->layoutData->items.size()) { *cursorPos = 0; return x.toReal(); } int pos = *cursorPos; int itm = eng->findItem(pos); if (pos == line.from + (int)line.length) { // end of line ensure we have the last item on the line itm = eng->findItem(pos-1); } const QScriptItem *si = &eng->layoutData->items[itm]; if (!si->num_glyphs) eng->shape(itm); pos -= si->position; QGlyphLayout *glyphs = eng->glyphs(si); unsigned short *logClusters = eng->logClusters(si); int l = eng->length(itm); if (pos > l) pos = l; if (pos < 0) pos = 0; int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos]; if (edge == Trailing) { // trailing edge is leading edge of next cluster while (glyph_pos < si->num_glyphs && !glyphs[glyph_pos].attributes.clusterStart) glyph_pos++; } bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2; int lineEnd = line.from + line.length; // add the items left of the cursor int firstItem = eng->findItem(line.from); int lastItem = eng->findItem(lineEnd - 1); int nItems = lastItem-firstItem+1; QVarLengthArray<int> visualOrder(nItems); QVarLengthArray<uchar> levels(nItems); for (int i = 0; i < nItems; ++i) levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); for (int i = 0; i < nItems; ++i) { int item = visualOrder[i]+firstItem; if (item == itm) break; QScriptItem &si = eng->layoutData->items[item]; if (!si.num_glyphs) eng->shape(item); if (si.isTab) { x = eng->nextTab(&si, x); continue; } else if (si.isObject) { x += si.width; continue; } int start = qMax(line.from, si.position); int end = qMin(lineEnd, si.position + eng->length(item)); logClusters = eng->logClusters(&si); int gs = logClusters[start-si.position]; int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1]; QGlyphLayout *glyphs = eng->glyphs(&si); while (gs <= ge) { x += (glyphs[gs].advance.x + QFixed::fromFixed(glyphs[gs].space_18d6)) * !glyphs[gs].attributes.dontPrint; ++gs; } } logClusters = eng->logClusters(si); glyphs = eng->glyphs(si); if (si->isTab) { if(pos == l) x = eng->nextTab(si, x); } else if (si->isObject) { if(pos == l) x += si->width; } else { if (reverse) { int end = qMin(lineEnd, si->position + l) - si->position; int glyph_end = end == l ? si->num_glyphs : logClusters[end]; for (int i = glyph_end - 1; i >= glyph_pos; i--) x += (glyphs[i].advance.x + QFixed::fromFixed(glyphs[i].space_18d6)) * !glyphs[i].attributes.dontPrint; } else { int start = qMax(line.from - si->position, 0); int glyph_start = logClusters[start]; for (int i = glyph_start; i < glyph_pos; i++) x += (glyphs[i].advance.x + QFixed::fromFixed(glyphs[i].space_18d6)) *!glyphs[i].attributes.dontPrint; } } *cursorPos = pos + si->position; return x.toReal();}/*! \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const Converts the x-coordinate \a x, to the nearest matching cursor position, depending on the cursor position type, \a cpos. \sa c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -