void CodeEditor::paintEvent(QPaintEvent* event) { QPlainTextEdit::paintEvent(event); QPainter painter(viewport()); painter.setPen(Qt::darkGray); QTextBlock block = firstVisibleBlock(); QRectF rect; do { if (!block.isVisible()) continue; rect = blockBoundingGeometry(block).translated(contentOffset()); QTextLine line = block.layout()->lineAt(0); if (config->whitespaces) { QString txt = block.text(); for (int i = 0; i < txt.length(); i++) { // rect.x() <- учитывая горизонтальный скролинг QPoint point(rect.x() + line.cursorToX(i), rect.y() + line.ascent()); if (txt[i] == ' ') painter.drawText(point, QChar(0x00b7)); else if (txt[i] == '\t') painter.drawText(point, QChar(0x21b9)); } } int state = block.userState(); if (!(state & Error) && state & Folded) { QRect collapseRect(rect.x() + line.rect().x() + line.naturalTextWidth() + FONTWIDTH * 2, rect.y() + 2, FONTWIDTH * 6, line.height() - 4); painter.drawText(collapseRect, Qt::AlignCenter, state & Comment ? "...;" : "...)"); painter.drawRoundedRect(collapseRect, 4, 6); } } while ((block = block.next()).isValid() && rect.y() < viewport()->height()); }
void QSvgText::draw(QPainter *p, QSvgExtraStates &states) { applyStyle(p, states); qreal oldOpacity = p->opacity(); p->setOpacity(oldOpacity * states.fillOpacity); // Force the font to have a size of 100 pixels to avoid truncation problems // when the font is very small. qreal scale = 100.0 / p->font().pointSizeF(); Qt::Alignment alignment = states.textAnchor; QTransform oldTransform = p->worldTransform(); p->scale(1 / scale, 1 / scale); qreal y = 0; bool initial = true; qreal px = m_coord.x() * scale; qreal py = m_coord.y() * scale; QSizeF scaledSize = m_size * scale; if (m_type == TEXTAREA) { if (alignment == Qt::AlignHCenter) px += scaledSize.width() / 2; else if (alignment == Qt::AlignRight) px += scaledSize.width(); } QRectF bounds; if (m_size.height() != 0) bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used. bool appendSpace = false; QVector<QString> paragraphs; QStack<QTextCharFormat> formats; QVector<QList<QTextLayout::FormatRange> > formatRanges; paragraphs.push_back(QString()); formatRanges.push_back(QList<QTextLayout::FormatRange>()); for (int i = 0; i < m_tspans.size(); ++i) { if (m_tspans[i] == LINEBREAK) { if (m_type == TEXTAREA) { if (paragraphs.back().isEmpty()) { QFont font = p->font(); font.setPixelSize(font.pointSizeF() * scale); QTextLayout::FormatRange range; range.start = 0; range.length = 1; range.format.setFont(font); formatRanges.back().append(range); paragraphs.back().append(QLatin1Char(' '));; } appendSpace = false; paragraphs.push_back(QString()); formatRanges.push_back(QList<QTextLayout::FormatRange>()); } } else { WhitespaceMode mode = m_tspans[i]->whitespaceMode(); m_tspans[i]->applyStyle(p, states); QFont font = p->font(); font.setPixelSize(font.pointSizeF() * scale); QString newText(m_tspans[i]->text()); newText.replace(QLatin1Char('\t'), QLatin1Char(' ')); newText.replace(QLatin1Char('\n'), QLatin1Char(' ')); bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' ')); if (appendSpace || prependSpace) paragraphs.back().append(QLatin1Char(' ')); bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' '))); if (mode == Default) { newText = newText.simplified(); if (newText.isEmpty()) appendSpaceNext = false; } QTextLayout::FormatRange range; range.start = paragraphs.back().length(); range.length = newText.length(); range.format.setFont(font); range.format.setTextOutline(p->pen()); range.format.setForeground(p->brush()); if (appendSpace) { Q_ASSERT(!formatRanges.back().isEmpty()); ++formatRanges.back().back().length; } else if (prependSpace) { --range.start; ++range.length; } formatRanges.back().append(range); appendSpace = appendSpaceNext; paragraphs.back() += newText; m_tspans[i]->revertStyle(p, states); } } if (states.svgFont) { // SVG fonts not fully supported... QString text = paragraphs.front(); for (int i = 1; i < paragraphs.size(); ++i) { text.append(QLatin1Char('\n')); text.append(paragraphs[i]); } states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor); } else { for (int i = 0; i < paragraphs.size(); ++i) { QTextLayout tl(paragraphs[i]); QTextOption op = tl.textOption(); op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); tl.setTextOption(op); tl.setAdditionalFormats(formatRanges[i]); tl.beginLayout(); forever { QTextLine line = tl.createLine(); if (!line.isValid()) break; if (m_size.width() != 0) line.setLineWidth(scaledSize.width()); } tl.endLayout(); bool endOfBoundsReached = false; for (int i = 0; i < tl.lineCount(); ++i) { QTextLine line = tl.lineAt(i); qreal x = 0; if (alignment == Qt::AlignHCenter) x -= 0.5 * line.naturalTextWidth(); else if (alignment == Qt::AlignRight) x -= line.naturalTextWidth(); if (initial && m_type == TEXT) y -= line.ascent(); initial = false; line.setPosition(QPointF(x, y)); // Check if the current line fits into the bounding rectangle. if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { // I need to set the bounds height to 'y-epsilon' to avoid drawing the current // line. Since the font is scaled to 100 units, 1 should be a safe epsilon. bounds.setHeight(y - 1); endOfBoundsReached = true; break; } y += 1.1 * line.height(); } tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds); if (endOfBoundsReached) break; } } p->setWorldTransform(oldTransform, false); p->setOpacity(oldOpacity); revertStyle(p, states); }
// FIXME: we need to figure out a way to derive from Fm::FolderItemDelegate to avoid code duplication. void DesktopItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_ASSERT(index.isValid()); QStyleOptionViewItemV4 opt = option; initStyleOption(&opt, index); painter->save(); painter->setClipRect(option.rect); opt.decorationAlignment = Qt::AlignHCenter | Qt::AlignTop; opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; // draw the icon QIcon::Mode iconMode; if(opt.state & QStyle::State_Enabled) { if(opt.state & QStyle::State_Selected) iconMode = QIcon::Selected; else { iconMode = QIcon::Normal; } } else iconMode = QIcon::Disabled; QPoint iconPos(opt.rect.x() + (opt.rect.width() - opt.decorationSize.width()) / 2, opt.rect.y()); QPixmap pixmap = opt.icon.pixmap(opt.decorationSize, iconMode); painter->drawPixmap(iconPos, pixmap); // draw some emblems for the item if needed // we only support symlink emblem at the moment FmFileInfo* file = static_cast<FmFileInfo*>(index.data(Fm::FolderModel::FileInfoRole).value<void*>()); if(file) { if(fm_file_info_is_symlink(file)) { painter->drawPixmap(iconPos, symlinkIcon_.pixmap(opt.decorationSize / 2, iconMode)); } } // draw text QRectF textRect(opt.rect.x(), opt.rect.y() + opt.decorationSize.height(), opt.rect.width(), opt.rect.height() - opt.decorationSize.height()); QTextLayout layout(opt.text, opt.font); QTextOption textOption; textOption.setAlignment(opt.displayAlignment); textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); textOption.setTextDirection(opt.direction); layout.setTextOption(textOption); qreal height = 0; qreal width = 0; int visibleLines = 0; layout.beginLayout(); QString elidedText; for(;;) { QTextLine line = layout.createLine(); if(!line.isValid()) break; line.setLineWidth(textRect.width()); height += opt.fontMetrics.leading(); line.setPosition(QPointF(0, height)); if((height + line.height() + textRect.y()) > textRect.bottom()) { // if part of this line falls outside the textRect, ignore it and quit. QTextLine lastLine = layout.lineAt(visibleLines - 1); elidedText = opt.text.mid(lastLine.textStart()); elidedText = opt.fontMetrics.elidedText(elidedText, opt.textElideMode, textRect.width()); break; } height += line.height(); width = qMax(width, line.naturalTextWidth()); ++ visibleLines; } layout.endLayout(); QRectF boundRect = layout.boundingRect(); boundRect.setWidth(width); boundRect.moveTo(textRect.x() + (textRect.width() - width)/2, textRect.y()); if((opt.state & QStyle::State_Selected) && opt.widget) { QPalette palette = opt.widget->palette(); // qDebug("w: %f, h:%f, m:%f", boundRect.width(), boundRect.height(), layout.minimumWidth()); painter->fillRect(boundRect, palette.highlight()); } else { // only draw shadow for non-selected items // draw shadow, FIXME: is it possible to use QGraphicsDropShadowEffect here? QPen prevPen = painter->pen(); painter->setPen(QPen(shadowColor_)); for(int i = 0; i < visibleLines; ++i) { QTextLine line = layout.lineAt(i); if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text QPointF pos(textRect.x() + line.position().x() + 1, textRect.y() + line.y() + line.ascent() + 1); painter->drawText(pos, elidedText); } else { line.draw(painter, textRect.topLeft() + QPointF(1, 1)); } } painter->setPen(prevPen); } // draw text for(int i = 0; i < visibleLines; ++i) { QTextLine line = layout.lineAt(i); if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text QPointF pos(textRect.x() + line.position().x(), textRect.y() + line.y() + line.ascent()); painter->drawText(pos, elidedText); } else { line.draw(painter, textRect.topLeft()); } } if(opt.state & QStyle::State_HasFocus) { // FIXME: draw focus rect } painter->restore(); }
// if painter is nullptr, the method calculate the bounding rectangle of the text and save it to textRect void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItemV4& opt, QRectF& textRect) const { QTextLayout layout(opt.text, opt.font); QTextOption textOption; textOption.setAlignment(opt.displayAlignment); textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); textOption.setTextDirection(opt.direction); layout.setTextOption(textOption); qreal height = 0; qreal width = 0; int visibleLines = 0; layout.beginLayout(); QString elidedText; for(;;) { QTextLine line = layout.createLine(); if(!line.isValid()) break; line.setLineWidth(textRect.width()); height += opt.fontMetrics.leading(); line.setPosition(QPointF(0, height)); if((height + line.height() + textRect.y()) > textRect.bottom()) { // if part of this line falls outside the textRect, ignore it and quit. QTextLine lastLine = layout.lineAt(visibleLines - 1); elidedText = opt.text.mid(lastLine.textStart()); elidedText = opt.fontMetrics.elidedText(elidedText, opt.textElideMode, textRect.width()); if(visibleLines == 1) // this is the only visible line width = textRect.width(); break; } height += line.height(); width = qMax(width, line.naturalTextWidth()); ++ visibleLines; } layout.endLayout(); // draw background for selected item QRectF boundRect = layout.boundingRect(); //qDebug() << "bound rect: " << boundRect << "width: " << width; boundRect.setWidth(width); boundRect.moveTo(textRect.x() + (textRect.width() - width)/2, textRect.y()); if(!painter) { // no painter, calculate the bounding rect only textRect = boundRect; return; } QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if(opt.state & QStyle::State_Selected) { painter->fillRect(boundRect, opt.palette.highlight()); painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); } else painter->setPen(opt.palette.color(cg, QPalette::Text)); // draw text for(int i = 0; i < visibleLines; ++i) { QTextLine line = layout.lineAt(i); if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text QPointF pos(textRect.x() + line.position().x(), textRect.y() + line.y() + line.ascent()); painter->drawText(pos, elidedText); } else { line.draw(painter, textRect.topLeft()); } } if(opt.state & QStyle::State_HasFocus) { // draw focus rect QStyleOptionFocusRect o; o.QStyleOption::operator=(opt); o.rect = boundRect.toRect(); // subElementRect(SE_ItemViewItemFocusRect, vopt, widget); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); if (const QWidget* widget = opt.widget) { QStyle* style = widget->style() ? widget->style() : qApp->style(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget); } } }
void QSvgText::draw(QPainter *p, QSvgExtraStates &states) { applyStyle(p, states); QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>( styleProperty(QSvgStyleProperty::FONT)); if (fontStyle && fontStyle->svgFont()) { // SVG fonts not fully supported... QString text = m_paragraphs.front(); for (int i = 1; i < m_paragraphs.size(); ++i) { text.append(QLatin1Char('\n')); text.append(m_paragraphs[i]); } fontStyle->svgFont()->draw(p, m_coord, text, fontStyle->pointSize(), m_textAlignment); revertStyle(p, states); return; } // Scale the font to its correct size. QTransform oldTransform = p->worldTransform(); p->scale(1 / m_scale, 1 / m_scale); qreal y = 0; bool initial = true; qreal px = m_coord.x() * m_scale; qreal py = m_coord.y() * m_scale; QSizeF scaledSize = m_size * m_scale; if (m_type == TEXTAREA) { if (m_textAlignment == Qt::AlignHCenter) px += scaledSize.width() / 2; else if (m_textAlignment == Qt::AlignRight) px += scaledSize.width(); } QRectF bounds; if (m_size.height() != 0) bounds = QRectF(0, 0, 1, scaledSize.height()); for (int i = 0; i < m_paragraphs.size(); ++i) { QTextLayout tl(m_paragraphs[i]); QTextOption op = tl.textOption(); op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); tl.setTextOption(op); tl.setAdditionalFormats(m_formatRanges[i]); tl.beginLayout(); forever { QTextLine line = tl.createLine(); if (!line.isValid()) break; if (m_size.width() != 0) line.setLineWidth(scaledSize.width()); } tl.endLayout(); bool endOfBoundsReached = false; for (int i = 0; i < tl.lineCount(); ++i) { QTextLine line = tl.lineAt(i); qreal x = 0; if (m_textAlignment == Qt::AlignHCenter) x -= line.naturalTextWidth() / 2; else if (m_textAlignment == Qt::AlignRight) x -= line.naturalTextWidth(); if (initial && m_type == TEXT) y -= line.ascent(); initial = false; line.setPosition(QPointF(x, y)); if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { bounds.setHeight(y); endOfBoundsReached = true; break; } y += 1.1 * line.height(); } tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds); if (endOfBoundsReached) break; } p->setWorldTransform(oldTransform, false); revertStyle(p, states); }
void KoTextShapeContainerModel::proposeMove(KoShape *child, QPointF &move) { Relation *relation = d->children.value(child); if (relation == 0 || relation->anchor == 0) return; QPointF newPosition = child->position() + move; QRectF parentShapeRect(QPointF(0, 0), child->parent()->size()); //kDebug(32500) <<"proposeMove:" << move <<" |" << newPosition <<" |" << parentShapeRect; if (qAbs(newPosition.x()) < 10) // align left relation->anchor->setAlignment(KoTextAnchor::Left); else if (qAbs(parentShapeRect.width() - newPosition.x()) < 10.0) relation->anchor->setAlignment(KoTextAnchor::Right); else if (qAbs(parentShapeRect.width() / 2.0 - newPosition.x()) < 10.0) relation->anchor->setAlignment(KoTextAnchor::Center); /*else { relation->anchor->setAlignment(KoTextAnchor::HorizontalOffset); // TODO //QPointF offset = relation->anchor->offset(); //offset.setX(offset.x() + move.x()); //relation->anchor->setOffset(offset); } */ if (qAbs(newPosition.y()) < 10.0) // TopOfFrame { kDebug(32500) <<" TopOfFrame"; relation->anchor->setAlignment(KoTextAnchor::TopOfFrame); } else if (qAbs(parentShapeRect.height() - newPosition.y()) < 10.0) { kDebug(32500) <<" BottomOfFrame"; relation->anchor->setAlignment(KoTextAnchor::BottomOfFrame); // TODO } else { // need layout info.. QTextBlock block = relation->anchor->document()->findBlock(relation->anchor->positionInDocument()); QTextLayout *layout = block.layout(); if (layout->lineCount() > 0) { KoTextShapeData *data = dynamic_cast<KoTextShapeData*>(child->parent()->userData()); Q_ASSERT(data); QTextLine tl = layout->lineAt(0); qreal y = tl.y() - data->documentOffset() - newPosition.y(); if (y >= 0 && y < 10) { kDebug(32500) <<" TopOfParagraph" << y <<""; relation->anchor->setAlignment(KoTextAnchor::TopOfParagraph); } else { tl = layout->lineAt(layout->lineCount() - 1); y = newPosition.y() - tl.y() - data->documentOffset() - tl.ascent(); if (y >= 0 && y < 10) { kDebug(32500) <<" BottomOfParagraph" << y; relation->anchor->setAlignment(KoTextAnchor::BottomOfParagraph); // TODO } else { tl = layout->lineForTextPosition(relation->anchor->positionInDocument() - block.position()); y = tl.y() - data->documentOffset() - newPosition.y(); if (y >= 0 && y < 10) { kDebug(32500) <<" AboveCurrentLine"; relation->anchor->setAlignment(KoTextAnchor::AboveCurrentLine); } //else do VerticalOffset here as well? } } } } move.setX(0); // let the text layout move it. move.setY(0); }