void FileMetaDataToolTip::setName(const QString& name) { QTextOption textOption; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); const QString processedName = Qt::mightBeRichText(name) ? name : KStringHandler::preProcessWrap(name); QTextLayout textLayout(processedName); textLayout.setFont(m_name->font()); textLayout.setTextOption(textOption); QString wrappedText; wrappedText.reserve(processedName.length()); // wrap the text to fit into the maximum width of m_name textLayout.beginLayout(); QTextLine line = textLayout.createLine(); while (line.isValid()) { line.setLineWidth(m_name->maximumWidth()); wrappedText += processedName.midRef(line.textStart(), line.textLength()); line = textLayout.createLine(); if (line.isValid()) { wrappedText += QChar::LineSeparator; } } textLayout.endLayout(); m_name->setText(wrappedText); }
int cursorPosition() const { if (!m_currentBlock.isValid()) return 0; int answer = m_currentBlock.position(); if (m_currentBlock.layout()->lineCount()) { QTextLine tl = m_currentBlock.layout()->lineAt(m_currentBlock.layout()->lineCount() - 1); answer += tl.textStart() + tl.textLength(); } return answer; }
int KTextDocumentLayout::hitTestIterated(QTextFrame::iterator begin, QTextFrame::iterator end, const QPointF &point, Qt::HitTestAccuracy accuracy) const { int position = -1; QTextFrame::iterator it = begin; for (it = begin; it != end; ++it) { QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); if (table) { QTextTableCell cell = m_state->hitTestTable(table, point); if (cell.isValid()) { position = hitTestIterated(cell.begin(), cell.end(), point, accuracy); if (position == -1) position = cell.lastPosition(); return position; } continue; } else if (subFrame) { position = hitTestIterated(subFrame->begin(), subFrame->end(), point, accuracy); if (position != -1) return position; continue; } else { if (!block.isValid()) continue; } // kDebug(32500) <<"hitTest[" << point.x() <<"," << point.y() <<"]"; QTextLayout *layout = block.layout(); if (point.y() > layout->boundingRect().bottom()) { // just skip this block. position = block.position() + block.length() - 1; continue; } for (int i = 0; i < layout->lineCount(); i++) { QTextLine line = layout->lineAt(i); // kDebug(32500) <<" + line[" << line.textStart() <<"]:" << line.y() <<"-" << line.height(); if (point.y() > line.y() + line.height()) { position = line.textStart() + line.textLength(); continue; } if (accuracy == Qt::ExactHit && point.y() < line.y()) // between lines return -1; if (accuracy == Qt::ExactHit && // left or right of line (point.x() < line.x() || point.x() > line.x() + line.width())) return -1; if (point.x() > line.width() && layout->textOption().textDirection() == Qt::RightToLeft) { // totally right of RTL text means the position is the start of the text. return block.position() + line.textStart(); } return block.position() + line.xToCursor(point.x()); } } return -1; }
int QQuickTextNodeEngine::addText(const QTextBlock &block, const QTextCharFormat &charFormat, const QColor &textColor, const QVarLengthArray<QTextLayout::FormatRange> &colorChanges, int textPos, int fragmentEnd, int selectionStart, int selectionEnd) { if (charFormat.foreground().style() != Qt::NoBrush) setTextColor(charFormat.foreground().color()); else setTextColor(textColor); while (textPos < fragmentEnd) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); } Q_ASSERT(line.textLength() > 0); int lineEnd = line.textStart() + block.position() + line.textLength(); int len = qMin(lineEnd - textPos, fragmentEnd - textPos); Q_ASSERT(len > 0); int currentStepEnd = textPos + len; addGlyphsForRanges(colorChanges, textPos - block.position(), currentStepEnd - block.position(), selectionStart - block.position(), selectionEnd - block.position()); textPos = currentStepEnd; } return textPos; }
void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color, QQuickText::TextStyle style, const QColor &styleColor, const QColor &anchorColor, const QColor &selectionColor, const QColor &selectedTextColor, int selectionStart, int selectionEnd, int lineStart, int lineCount) { QQuickTextNodeEngine engine; engine.setTextColor(color); engine.setSelectedTextColor(selectedTextColor); engine.setSelectionColor(selectionColor); engine.setAnchorColor(anchorColor); engine.setPosition(position); #if QT_CONFIG(im) int preeditLength = textLayout->preeditAreaText().length(); int preeditPosition = textLayout->preeditAreaPosition(); #endif QVarLengthArray<QTextLayout::FormatRange> colorChanges; engine.mergeFormats(textLayout, &colorChanges); lineCount = lineCount >= 0 ? qMin(lineStart + lineCount, textLayout->lineCount()) : textLayout->lineCount(); for (int i=lineStart; i<lineCount; ++i) { QTextLine line = textLayout->lineAt(i); int start = line.textStart(); int length = line.textLength(); int end = start + length; #if QT_CONFIG(im) if (preeditPosition >= 0 && preeditPosition >= start && preeditPosition < end) { end += preeditLength; } #endif engine.setCurrentLine(line); engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); } engine.addToSceneGraph(this, style, styleColor); }
// Origin: Qt static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, qreal &widthUsed) { height = 0; widthUsed = 0; textLayout.beginLayout(); QString str = textLayout.text(); while (true) { QTextLine line = textLayout.createLine(); if (!line.isValid()) break; if (line.textLength() == 0) break; line.setLineWidth(lineWidth); line.setPosition(QPointF(0, height)); height += line.height(); widthUsed = qMax(widthUsed, line.naturalTextWidth()); } textLayout.endLayout(); }
QStringList StatusEventItemDelegate::layoutText(const QString &text, const QFont &font, int maxLineWidth, int maxLines, int *textHeight) { QTextLayout textLayout(text, font); QFontMetrics fontMetrics(font); QStringList lines; qreal height = 0.0; textLayout.beginLayout(); while (lines.size() < maxLines) { QTextLine line = textLayout.createLine(); if (! line.isValid()) break; if (maxLines <= 0 || lines.size() < maxLines-1) { // Wrap the current line at or below the maximum line width line.setLineWidth(maxLineWidth); lines.append(text.mid(line.textStart(), line.textLength())); } else { // Set the line width beyond the max line width, and then elide it // so the user has a visible indication that the full message is // longer than what is visible. line.setLineWidth(2 * maxLineWidth); lines.append(fontMetrics.elidedText(text.mid(line.textStart()), Qt::ElideRight, maxLineWidth)); } height += fontMetrics.leading() + line.height(); } textLayout.endLayout(); if (textHeight) *textHeight = qRound(height); return lines; }
void KStandardItemListWidget::updateIconsLayoutTextCache() { // +------+ // | Icon | // +------+ // // Name role that // might get wrapped above // several lines. // Additional role 1 // Additional role 2 const QHash<QByteArray, QVariant> values = data(); const KItemListStyleOption& option = styleOption(); const qreal padding = option.padding; const qreal maxWidth = size().width() - 2 * padding; const qreal widgetHeight = size().height(); const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); // Initialize properties for the "text" role. It will be used as anchor // for initializing the position of the other roles. TextInfo* nameTextInfo = m_textInfo.value("text"); const QString nameText = KStringHandler::preProcessWrap(values["text"].toString()); nameTextInfo->staticText.setText(nameText); // Calculate the number of lines required for the name and the required width qreal nameWidth = 0; qreal nameHeight = 0; QTextLine line; const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); const int maxNameLines = (option.maxTextSize.height() / int(lineSpacing)) - additionalRolesCount; QTextLayout layout(nameTextInfo->staticText.text(), m_customizedFont); layout.setTextOption(nameTextInfo->staticText.textOption()); layout.beginLayout(); int nameLineIndex = 0; while ((line = layout.createLine()).isValid()) { line.setLineWidth(maxWidth); nameWidth = qMax(nameWidth, line.naturalTextWidth()); nameHeight += line.height(); ++nameLineIndex; if (nameLineIndex == maxNameLines) { // The maximum number of textlines has been reached. If this is // the case provide an elided text if necessary. const int textLength = line.textStart() + line.textLength(); if (textLength < nameText.length()) { // Elide the last line of the text QString lastTextLine = nameText.mid(line.textStart(), line.textLength()); lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, Qt::ElideRight, line.naturalTextWidth() - 1); const QString elidedText = nameText.left(line.textStart()) + lastTextLine; nameTextInfo->staticText.setText(elidedText); } break; } } layout.endLayout(); // Use one line for each additional information nameTextInfo->staticText.setTextWidth(maxWidth); nameTextInfo->pos = QPointF(padding, widgetHeight - nameHeight - additionalRolesCount * lineSpacing - padding); m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, nameTextInfo->pos.y(), nameWidth, nameHeight); // Calculate the position for each additional information qreal y = nameTextInfo->pos.y() + nameHeight; foreach (const QByteArray& role, m_sortedVisibleRoles) { if (role == "text") { continue; } const QString text = roleText(role, values); TextInfo* textInfo = m_textInfo.value(role); textInfo->staticText.setText(text); qreal requiredWidth = 0; QTextLayout layout(text, m_customizedFont); QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); layout.setTextOption(textOption); layout.beginLayout(); QTextLine textLine = layout.createLine(); if (textLine.isValid()) { textLine.setLineWidth(maxWidth); requiredWidth = textLine.naturalTextWidth(); if (requiredWidth > maxWidth) { const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); textInfo->staticText.setText(elidedText); requiredWidth = m_customizedFontMetrics.width(elidedText); } else if (role == "rating") { // Use the width of the rating pixmap, because the rating text is empty. requiredWidth = m_rating.width(); } } layout.endLayout(); textInfo->pos = QPointF(padding, y); textInfo->staticText.setTextWidth(maxWidth); const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); m_textRect |= textRect; y += lineSpacing; } // Add a padding to the text rectangle m_textRect.adjust(-padding, -padding, padding, padding); }
QRect StyleHelper::drawText(QPainter* p, const QRect& rc, QString& str, int nLines, int nFlags, const QColor& color, const QFont& font, bool bElided) { if (str.isEmpty()) { qDebug() << "[WARNING]: the text should not be empty when drawing!"; return QRect(); } QFontMetrics fm(font); if (rc.height() < (fm.height() + fm.leading()) * nLines) { qDebug() << "[WARNING]: space is not enough for drawing! text: " << str.left(30) << "..."; } //if (rc.width() * nLines < fm.width(str)) { // qDebug() << "[WARNING]: width should bigger than font metrics when drawing! text:" << str.left(30) << "..."; //} p->save(); p->setPen(color); p->setFont(font); int nWidth = 0; int nHeight = 0; int nHeightLine = p->fontMetrics().height() + leading(); QRect rcRet(rc.x(), rc.y(), rc.width(), nHeightLine); rcRet.adjust(margin(), 0, -margin(), 0); QTextLayout textLayout(str, p->font()); QTextOption opt = textLayout.textOption(); opt.setWrapMode(QTextOption::WrapAnywhere); textLayout.setTextOption(opt); textLayout.beginLayout(); while (nLines) { QTextLine line = textLayout.createLine(); if (!line.isValid()) { break; } line.setLineWidth(rcRet.width()); QString lineText; if (nLines == 1 && bElided) { // the last line lineText = p->fontMetrics().elidedText(str, Qt::ElideRight, rcRet.width()); nWidth = qMax<int>(p->fontMetrics().width(lineText), nWidth); } else { lineText = str.left(line.textLength()); nWidth = qMax<int>(line.width(), nWidth); } str.remove(0, line.textLength()); p->drawText(rcRet, nFlags, lineText); nHeight += nHeightLine; rcRet.setRect(rc.x(), rc.y() + nHeight, nWidth, nHeightLine); rcRet.adjust(margin(), 0, -margin(), 0); nLines--; } textLayout.endLayout(); rcRet.setRect(rc.x() + margin(), rc.y(), nWidth + margin(), nHeight); //rcRet.adjust(margin(), 0, -margin(), 0); p->restore(); return rcRet; }
ReportItem::PrintResult ReportItemPara::printMetaPaintChildren(ReportItemMetaPaint *out, const ReportItem::Rect &bounding_rect) { qfLogFuncFrame() << this << bounding_rect.toString(); PrintResult res = PR_PrintedOk; if(m_indexToPrint == 0) { printedText = paraText(); } //qfInfo() << printedText; QString text = printedText.mid(m_indexToPrint); int initial_index_to_print = m_indexToPrint; QString sql_id = sqlId(); /// tiskne se prazdny text bool omit_empty_text = isOmitEmptyText(); if(text.isEmpty() && omit_empty_text) { } else { QString text_to_layout = text; //qfWarning() << "length: " << text.length() << " text: [" << text << "]\n" << text.toUtf8().toHex(); bool text_item_should_be_created = true; style::CompiledTextStyle style; style::Text *p_text_style = effectiveTextStyle(); if(p_text_style) { style = p_text_style->textStyle(); } QFontMetricsF font_metrics = processor()->fontMetrics(style.font()); QTextOption text_option; { if(isTextWrap()) text_option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); //alignment_flags |= Qt::TextWordWrap; int al = textHAlign() | textVAlign(); Qt::Alignment alignment_flags = (Qt::Alignment)al; text_option.setAlignment(alignment_flags); } Rect rendered_bounding_rect; /// velikost boundingRect je v mm, tak to prepocitej na body vystupniho zarizeni rendered_bounding_rect = qmlwidgets::graphics::mm2device(bounding_rect, processor()->paintDevice()); bool render_check_mark = false; QRegExp rx = ReportItemMetaPaint::checkReportSubstitutionRegExp; if(rx.exactMatch(text_to_layout)) { //bool check_on = rx.capturedTexts().value(1) == "1"; rendered_bounding_rect = font_metrics.boundingRect('X'); render_check_mark = true; m_indexToPrint += text_to_layout.length(); } else { if(text_to_layout.isEmpty()) { /// neni omitEmptyString, takze i prazdnej text vyrendruj alespon jako mezeru aby se na to dalo treba kliknout text_to_layout = ' '; } //text.replace(ReportItemMetaPaint::checkOnReportSubstitution, "X"); //text.replace(ReportItemMetaPaint::checkOffReportSubstitution, "X"); //qfInfo().noSpace().color(QFLog::Green) << "index to print: " << indexToPrint << " text: '" << text << "'"; //qfInfo() << "bounding rect:" << bounding_rect.toString(); //qfWarning() << "device physical DPI:" << processor()->paintDevice()->physicalDpiX() << processor()->paintDevice()->physicalDpiY(); //qfWarning().noSpace() << "'" << text << "' font metrics: " << br.toString(); //QString text = element.text().simplified().replace("\\n", "\n"); //qfInfo() << "br:" << br.toString(); //Rect br_debug = br; //bool splitted = false; /// do layout { qreal leading = font_metrics.leading(); qreal height = 0; qreal width = 0; textLayout.setFont(style.font()); textLayout.setTextOption(text_option); textLayout.setText(text_to_layout); textLayout.beginLayout(); bool finished = false; while (!finished) { QTextLine line = textLayout.createLine(); finished = !line.isValid(); if(!finished) { line.setLineWidth(rendered_bounding_rect.width()); /// setWidth() nastavi spravne line.height(), proto musi byt pred merenim popsane vysky. if((line.textLength() == 0) && (line.textStart() + line.textLength() == text_to_layout.length())) { /// nevim kde je chyba, pri vicerakovych textech mi to pridava jeden prazdnej radek na konec, takhle se tomu snazim zabranit (Qt 4.6.3) finished = true; } else { qreal interline_space = (height > 0)? leading: 0; if(height + interline_space + line.height() > rendered_bounding_rect.height()) { res = PR_PrintAgainOnNextPage; if(height == 0) { /// nevejde se ani jeden radek text_item_should_be_created = false; break; } else { /// neco se preci jenom veslo int pos = line.textStart(); m_indexToPrint += pos; break; } } height += interline_space; line.setPosition(QPointF(0., height)); height += line.height(); width = qMax(width, line.naturalTextWidth()); } } if(finished) { m_indexToPrint = printedText.length(); } } textLayout.endLayout(); rendered_bounding_rect.setWidth(width); rendered_bounding_rect.setHeight(height); } } /// velikost boundingRect je v bodech vystupniho zarizeni, tak to prepocitej na mm rendered_bounding_rect = qmlwidgets::graphics::device2mm(rendered_bounding_rect, processor()->paintDevice()); /// rendered rect is left aligned, if text is reight aligned or centered, the ReportItemMetaPaintText::paint() does it if(text_item_should_be_created ) { ReportItemMetaPaintText *mt; if(render_check_mark ) mt = new ReportItemMetaPaintCheck(out, this); else { mt = new ReportItemMetaPaintText(out, this); mt->sqlId = sql_id; //--mt->editGrants = elementAttribute("editGrants"); } //qfInfo() << "creating item:" << mt; mt->pen = style.pen(); mt->font = style.font(); mt->text = text.mid(0, m_indexToPrint - initial_index_to_print); //qfWarning() << "text:" << text; mt->textOption = text_option; mt->renderedRect = rendered_bounding_rect; mt->renderedRect.flags = designedRect.flags; } //qfDebug().color(QFLog::Green, QFLog::Red) << "\tleading:" << processor()->fontMetrics(style.font).leading() << "\theight:" << processor()->fontMetrics(style.font).height(); qfDebug() << "\tchild rendered rect:" << rendered_bounding_rect.toString(); } qfDebug() << "\t<<< CHILDREN paraText return:" << res.toString(); return res; }
void BaseEditor::paintEvent(QPaintEvent *e) { //copy from QPlainTextEditor QPainter painter(viewport()); Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout())); QPointF offset(contentOffset()); QRect er = e->rect(); QRect viewportRect = viewport()->rect(); bool editable = !isReadOnly(); QTextBlock block = firstVisibleBlock(); qreal maximumWidth = document()->documentLayout()->documentSize().width(); //margin qreal lineX = 0; if (conf->isDisplayRightColumnMargin()) { // Don't use QFontMetricsF::averageCharWidth here, due to it returning // a fractional size even when this is not supported by the platform. lineX = QFontMetricsF(document()->defaultFont()).width(QLatin1Char('X')) * conf->getRightMarginColumn() + offset.x() + 4; if (lineX < viewportRect.width()) { const QBrush background = QBrush(QColor(239, 239, 239)); painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()), background); const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white; const QPen pen = painter.pen(); painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(), col, 32)); painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom())); painter.setPen(pen); } } // Set a brush origin so that the WaveUnderline knows where the wave started painter.setBrushOrigin(offset); // keep right margin clean from full-width selection int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth) - document()->documentMargin(); er.setRight(qMin(er.right(), maxX)); painter.setClipRect(er); QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); while (block.isValid()) { QRectF r = blockBoundingRect(block).translated(offset); QTextLayout *layout = block.layout(); if (!block.isVisible()) { offset.ry() += r.height(); block = block.next(); continue; } if (r.bottom() >= er.top() && r.top() <= er.bottom()) { QTextBlockFormat blockFormat = block.blockFormat(); QBrush bg = blockFormat.background(); if (bg != Qt::NoBrush) { QRectF contentsRect = r; contentsRect.setWidth(qMax(r.width(), maximumWidth)); fillBackground(&painter, contentsRect, bg); } QVector<QTextLayout::FormatRange> selections; int blpos = block.position(); int bllen = block.length(); for (int i = 0; i < context.selections.size(); ++i) { const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); const int selStart = range.cursor.selectionStart() - blpos; const int selEnd = range.cursor.selectionEnd() - blpos; if (selStart < bllen && selEnd > 0 && selEnd > selStart) { QTextLayout::FormatRange o; o.start = selStart; o.length = selEnd - selStart; o.format = range.format; selections.append(o); } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) && block.contains(range.cursor.position())) { // for full width selections we don't require an actual selection, just // a position to specify the line. that's more convenience in usage. QTextLayout::FormatRange o; QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); o.start = l.textStart(); o.length = l.textLength(); if (o.start + o.length == bllen - 1) ++o.length; // include newline o.format = range.format; selections.append(o); } } bool drawCursor = ((editable || (textInteractionFlags() & Qt::TextSelectableByKeyboard)) && context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen); bool drawCursorAsBlock = drawCursor && overwriteMode() ; if (drawCursorAsBlock) { if (context.cursorPosition == blpos + bllen - 1) { drawCursorAsBlock = false; } else { QTextLayout::FormatRange o; o.start = context.cursorPosition - blpos; o.length = 1; o.format.setForeground(palette().base()); o.format.setBackground(palette().text()); selections.append(o); } } layout->draw(&painter, offset, selections, er); if ((drawCursor && !drawCursorAsBlock) || (editable && context.cursorPosition < -1 && !layout->preeditAreaText().isEmpty())) { int cpos = context.cursorPosition; if (cpos < -1) cpos = layout->preeditAreaPosition() - (cpos + 2); else cpos -= blpos; layout->drawCursor(&painter, offset, cpos, cursorWidth()); } } offset.ry() += r.height(); if (offset.y() > viewportRect.height()) break; block = block.next(); } if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); } }