void TestDocumentLayout::noRunAroundFrame() { // With this test we want to make sure a shape that is set to not run around // will simply put the text further down. initForNewTest(loremIpsum); MockShape *picture = new MockShape(); KWFrame frame(picture, frameSet); frame.setTextRunAround(KWord::NoRunAround); picture->setSize(QSizeF(100, 100)); picture->setPosition(QPointF(0, 0)); MockLayoutState *state = new MockLayoutState(doc); layout->setLayout(state); state->shape = shape1; layout->layout(); QTextLayout *lay = doc->begin().layout(); QVERIFY(lay->lineCount() >= 4); QTextLine line = doc->begin().layout()->lineAt(0); QVERIFY(line.isValid()); double preY = line.position().y(); int linenumber=1; line = doc->begin().layout()->lineAt(linenumber); while(linenumber < lay->lineCount()) { qDebug() << line.position().y() << (preY + 14.4); QVERIFY(line.position().y() > (preY + 14.4 - ROUNDING)); preY = line.position().y(); ++linenumber; line = doc->begin().layout()->lineAt(linenumber); } }
void TestDocumentLayout::placeAnchoredFrame3() { // basic inline frame that acts like a really big character initForNewTest(QString(loremIpsum)); MockShape *picture = new MockShape(); picture->setSize(QSizeF(100, 100)); KTextAnchor *anchor = new KTextAnchor(picture); anchor->setAlignment(KTextAnchor::VerticalOffset); anchor->setAlignment(KTextAnchor::HorizontalOffset); QTextCursor cursor(doc); KInlineTextObjectManager *manager = new KInlineTextObjectManager(); layout->setInlineTextObjectManager(manager); MockLayoutState *state = new MockLayoutState(doc); layout->setLayout(state); state->shape = shape1; manager->insertInlineObject(cursor, anchor); layout->layout(); /* I have two goals with 'offset'. One is that I want to be able to change the baseline of my anchored object. The other is that OOo / ODF allows me to have an arbitairy distance from my anchor so I can place something at the center of my page or whatever. So what about I switch from the first to the latter based on the font height. If my offset 'x' != 0, make the image floating. If my offset 'y' is such that it would be above or below my line; make floating. */ QTextLayout *lay = doc->begin().layout(); QVERIFY(lay->lineCount() >= 2); QTextLine line = lay->lineAt(0); QCOMPARE(line.descent(), (qreal) 100); QCOMPARE(line.position(), QPointF()); line = lay->lineAt(1); QVERIFY(line.height() < 20); // now move the character which makes it a shape to run around and no longer // a big character. anchor->setOffset(QPointF(50, 20)); layout->layout(); lay = doc->begin().layout(); QVERIFY(lay->lineCount() >= 2); line = lay->lineAt(0); QVERIFY(line.height() < 20); QCOMPARE(line.position(), QPointF()); line = lay->lineAt(1); QVERIFY(line.height() < 20); QCOMPARE(line.position().x(), 0.); QVERIFY(qAbs(line.position().y() - 14.4) < 0.125); }
void KTextDocumentLayout::Private::adjustSize() { if (parent->resizeMethod() == KTextDocument::NoResize) return; if (parent->shapes().isEmpty()) return; // Limit auto-resizing to the first shape only (there won't be more // with auto-resizing turned on, unless specifically set) KShape *shape = parent->shapes().first(); // Determine the maximum width of all text lines qreal width = 0; for (QTextBlock block = parent->document()->begin(); block.isValid(); block = block.next()) { // The block layout's wrap mode must be QTextOption::NoWrap, thus the line count // of a valid block must be 1 (otherwise this resizing scheme wouldn't work) Q_ASSERT(block.layout()->lineCount() == 1); QTextLine line = block.layout()->lineAt(0); width = qMax(width, line.naturalTextWidth()); } // Use position and height of last text line to calculate height QTextLine line = parent->document()->lastBlock().layout()->lineAt(0); qreal height = line.position().y() + line.height(); shape->setSize(QSizeF(width, height)); }
void TextLabel::drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect) { if (rect.width() < 1 || rect.height() < 1) { return; } QPixmap pixmap(rect.size()); pixmap.fill(Qt::transparent); QPainter p(&pixmap); p.setPen(painter->pen()); // Create the alpha gradient for the fade out effect QLinearGradient alphaGradient(0, 0, 1, 0); alphaGradient.setCoordinateMode(QGradient::ObjectBoundingMode); if (layout.textOption().textDirection() == Qt::LeftToRight) { alphaGradient.setColorAt(0, QColor(0, 0, 0, 255)); alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); } else { alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); alphaGradient.setColorAt(1, QColor(0, 0, 0, 255)); } QFontMetrics fm(layout.font()); int textHeight = layout.lineCount() * fm.lineSpacing(); QPointF position(0, (rect.height() - textHeight) / 2); QList<QRect> fadeRects; int fadeWidth = 30; // Draw each line in the layout for (int i = 0; i < layout.lineCount(); i++) { QTextLine line = layout.lineAt(i); line.draw(&p, position); // Add a fade out rect to the list if the line is too long if (line.naturalTextWidth() > rect.width()) { int x = int(qMin(line.naturalTextWidth(), (qreal)pixmap.width())) - fadeWidth; int y = int(line.position().y() + position.y()); QRect r = QStyle::visualRect(layout.textOption().textDirection(), pixmap.rect(), QRect(x, y, fadeWidth, int(line.height()))); fadeRects.append(r); } } // Reduce the alpha in each fade out rect using the alpha gradient if (!fadeRects.isEmpty()) { p.setCompositionMode(QPainter::CompositionMode_DestinationIn); foreach (const QRect &rect, fadeRects) { p.fillRect(rect, alphaGradient); }
QPainterPath Text::shape() const { QPainterPath pp; #if 0 for (QTextBlock tb = doc()->begin(); tb.isValid(); tb = tb.next()) { QTextLayout* tl = tb.layout(); int n = tl->lineCount(); for (int i = 0; i < n; ++i) { QTextLine l = tl->lineAt(i); QRectF r(l.naturalTextRect().translated(tl->position())); r.adjust(-l.position().x(), 0.0, 0.0, 0.0); pp.addRect(r); } } #endif return pp; }
// 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(); }
void TestTableLayout::testBasicLayout() { QList<KoTableColumnStyle *> columnStyles; QList<KoTableRowStyle *> rowStyles; QMap<QPair<int, int>, KoTableCellStyle *> cellStyles; QMap<QPair<int, int>, QString> cellTexts; cellTexts.insert(qMakePair(0, 0), "Cell 1"); cellTexts.insert(qMakePair(0, 1), "Cell 2"); cellTexts.insert(qMakePair(1, 0), "Cell 3"); cellTexts.insert(qMakePair(1, 1), "Cell 4"); initTest(2, 2, 0, columnStyles, rowStyles, cellStyles, cellTexts); m_layout->layout(); // Check that the table and layout data was correctly added to the table data map. QVERIFY(m_textLayout->m_tableLayout.m_tableLayoutDataMap.contains(m_table)); TableLayoutData *tableLayoutData = m_textLayout->m_tableLayout.m_tableLayoutDataMap.value(m_table); QVERIFY(tableLayoutData); // Check table dimensions are correct. QCOMPARE(tableLayoutData->m_rowPositions.size(), 2); QCOMPARE(tableLayoutData->m_rowHeights.size(), 2); QCOMPARE(tableLayoutData->m_tableRects.last().columnPositions.size(), 2); QCOMPARE(tableLayoutData->m_tableRects.last().columnWidths.size(), 2); // Check cell bounding rectangles. /* * Cell 0, 0 rules: * x = 0 (no borders/margins/paddings) * y = 0 (no borders/margins/paddings) * width = 200/2 = 100 (table width/column count) * height = 1 * 14.4 = 14.4 (number of lines * line height) */ QTextTableCell cell1 = m_table->cellAt(0, 0); QCOMPARE(m_textLayout->m_tableLayout.cellBoundingRect(cell1), QRectF(0, 0, 100, 14.4)); /* * Cell 0, 1 rules: * x = 100 (table width/column count) * y = 0 (no borders/margins/paddings) * width = 200/2 = 100 (table width/column count) * height = 1 * 14.4 = 14.4 (number of lines * line height) */ QTextTableCell cell2 = m_table->cellAt(0, 1); QCOMPARE(m_textLayout->m_tableLayout.cellBoundingRect(cell2), QRectF(100, 0, 100, 14.4)); /* * Cell 1, 0 rules: * x = 0 (no borders/margins/paddings) * y = 14.4 (line height) * width = 200/2 = 100 (table width/column count) * height = 1 * 14.4 = 14.4 (number of lines * line height) */ QTextTableCell cell3 = m_table->cellAt(1, 0); QCOMPARE(m_textLayout->m_tableLayout.cellBoundingRect(cell3), QRectF(0, 14.4, 100, 14.4)); /* * Cell 1, 1 rules: * x = 100 (table width/column count) * y = 14.4 (line height) * width = 200/2 = 100 (table width/column count) * height = 1 * 14.4 = 14.4 (number of lines * line height) */ QTextTableCell cell4 = m_table->cellAt(1, 1); QCOMPARE(m_textLayout->m_tableLayout.cellBoundingRect(cell4), QRectF(100, 14.4, 100, 14.4)); // Check position of blocks in cells. QTextBlock block1 = cell1.firstCursorPosition().block(); QCOMPARE(block1.position(), 1); QVERIFY(block1.layout()); QCOMPARE(block1.layout()->lineCount(), 1); QTextLine line = block1.layout()->lineAt(0); QCOMPARE(line.width(), 100.); QCOMPARE(line.position(), QPointF()); /* * Blocks in cell 0,1 rules: * Position Content Layout bounding rect * 8 "Cell 2" 100, 0 100x? */ QTextBlock block2 = cell2.firstCursorPosition().block(); QCOMPARE(block2.position(), 8); QVERIFY(block2.layout()); QCOMPARE(block2.layout()->lineCount(), 1); line = block2.layout()->lineAt(0); QCOMPARE(line.width(), 100.); QCOMPARE(line.position(), QPointF(100, 0)); /* * Blocks in cell 1,0 rules: * Position Content Layout bounding rect * 15 "Cell 3" 0, 14.4 100x? */ QTextBlock block3 = cell3.firstCursorPosition().block(); QCOMPARE(block3.position(), 15); QVERIFY(block3.layout()); QCOMPARE(block3.layout()->lineCount(), 1); line = block3.layout()->lineAt(0); QCOMPARE(line.width(), 100.); QCOMPARE(line.position().x(), 0.); QVERIFY(qAbs(line.position().y() - 14.4) < 0.156); /* * Blocks in cell 1,1 rules: * Position Content Layout bounding rect * 22 "Cell 4" 100, 14.4 100x? */ QTextBlock block4 = cell4.firstCursorPosition().block(); QCOMPARE(block4.position(), 22); QVERIFY(block4.layout()); QCOMPARE(block4.layout()->lineCount(), 1); line = block4.layout()->lineAt(0); QCOMPARE(line.width(), 100.); QCOMPARE(line.position().x(), 100.); QVERIFY(qAbs(line.position().y() - 14.4) < 0.156); /* * TODO: Insert/remove rows/columns. */ cleanupTest(); }
// 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); } } }