void TestDocumentLayout::testCenteredItems() { initForNewTest("ListItem\nListItem\nListItem"); KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); listStyle.setLevelProperties(llp); QTextBlock block = m_doc->begin(); // normal block QVERIFY(block.isValid()); listStyle.applyStyle(block); block = block.next(); // centered block QVERIFY(block.isValid()); listStyle.applyStyle(block); QTextBlockFormat fmt; fmt.setAlignment(Qt::AlignHCenter); QTextCursor cursor(block); cursor.mergeBlockFormat(fmt); block = block.next(); // centered RTL text. listStyle.applyStyle(block); cursor = QTextCursor(block); fmt.setProperty(KoParagraphStyle::TextProgressionDirection, KoText::RightLeftTopBottom); cursor.mergeBlockFormat(fmt); m_layout->layout(); block = m_doc->begin(); QTextLayout *layout = block.layout(); QTextLine line1 = layout->lineAt(0); KoTextBlockData *data1 = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(line1.isValid()); QVERIFY(line1.width() < 200); // the counter takes some space. block = block.next(); layout = block.layout(); QTextLine line2 = layout->lineAt(0); KoTextBlockData *data2 = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(line2.isValid()); QVERIFY(line2.width() < 200); // the counter takes some space. QCOMPARE(line1.width(), line2.width()); const qreal width1 = line1.naturalTextWidth() + data1->counterWidth() + data1->counterSpacing(); const qreal width2 = line2.naturalTextWidth() + data2->counterWidth() + data2->counterSpacing(); QCOMPARE(width1, width2); QVERIFY(data1->counterPosition().x() < data2->counterPosition().x()); const qreal padding = (200 - width2) / 2; QVERIFY(padding > 0);// not really a layout test, but the rest will be bogus otherwise. QCOMPARE(data2->counterPosition().x(), padding); // close to the centered text. // right to left parag places the counter on the right. Its centered, so not the far right. block = block.next(); layout = block.layout(); QTextLine line = layout->lineAt(0); KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QCOMPARE(data->counterPosition().x(), 200 - padding - data->counterWidth()); }
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 YTDelegate::layoutText(QTextLayout& textLayout, QString text, QSize constraint) const { QTextOption textOption(Qt::AlignJustify); textLayout.setTextOption(textOption); textLayout.setText(text); textLayout.beginLayout(); int lHeight = 0; while(true){ QTextLine line = textLayout.createLine(); if(!line.isValid()) break; line.setLineWidth(constraint.width()); line.setPosition(QPointF(0, lHeight)); if(lHeight + line.height() > constraint.height()) { QTextLine lastLine = textLayout.lineAt(textLayout.lineCount() - 2); QString lastString = text.mid(lastLine.textStart()); QFontMetrics fm(textLayout.font()); text.chop(lastString.length()); text += fm.elidedText(lastString, Qt::ElideRight, constraint.width()-1); textLayout.endLayout(); layoutText(textLayout, text, constraint); return; } lHeight += line.height(); lHeight += line.leading(); } textLayout.endLayout(); }
void tst_QTextFormat::testTabsUsed() { QTextDocument doc; QTextCursor cursor(&doc); QList<QTextOption::Tab> tabs; QTextBlockFormat format; QTextOption::Tab tab; tab.position = 100; tabs.append(tab); format.setTabPositions(tabs); cursor.mergeBlockFormat(format); cursor.insertText("foo\tbar"); //doc.setPageSize(QSizeF(200, 200)); doc.documentLayout()->pageCount(); // force layout; QTextBlock block = doc.begin(); QTextLayout *layout = block.layout(); QVERIFY(layout); QCOMPARE(layout->lineCount(), 1); QTextLine line = layout->lineAt(0); QCOMPARE(line.cursorToX(4), 100.); QTextOption option = layout->textOption(); QCOMPARE(option.tabs().count(), tabs.count()); }
QList<QGlyphRun> QTextFragment::glyphRuns(int pos, int len) const { if (!p || !n) return QList<QGlyphRun>(); int blockNode = p->blockMap().findNode(position()); const QTextBlockData *blockData = p->blockMap().fragment(blockNode); QTextLayout *layout = blockData->layout; int blockPosition = p->blockMap().position(blockNode); if (pos < 0) pos = position() - blockPosition; if (len < 0) len = length(); if (len == 0) return QList<QGlyphRun>(); QList<QGlyphRun> ret; for (int i=0; i<layout->lineCount(); ++i) { QTextLine textLine = layout->lineAt(i); ret += textLine.glyphRuns(pos, len); } return ret; }
void setContent(const ToolTipContent &data) { QString html; if (!data.mainText().isEmpty()) { html.append("<div><b>" + data.mainText() + "</b></div>"); } html.append(data.subText()); m_anchor.clear(); m_document->clear(); data.registerResources(m_document); if (!html.isEmpty()) { m_document->setHtml("<p>" + html + "</p>"); } m_document->adjustSize(); m_haloRects.clear(); QTextLayout *layout = m_document->begin().layout(); //layout->setPosition(QPointF(textRect.x(), textBoundingRect->y())); QTextLine line; for (int i = 0; i < layout->lineCount(); ++i) { line = layout->lineAt(i); // Add halo rect only when a non empty line is found if (line.naturalTextWidth()) { m_haloRects.append(line.naturalTextRect().translated(layout->position().toPoint()).toRect().translated(m_margin, m_margin)); } } update(); }
void TestDocumentLayout::testBasicList() { initForNewTest("Base\nListItem\nListItem2: The quick brown fox jums over the lazy dog.\nNormal\nNormal"); KoParagraphStyle style; QTextBlock block = m_doc->begin(); style.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); KoListStyle listStyle; KoListLevelProperties level1; level1.setStyle(KoListStyle::Bullet); listStyle.setLevelProperties(level1); style.setListStyle(&listStyle); style.applyStyle(block); // make this a listStyle QVERIFY(block.textList()); QCOMPARE(block.textList()->format().intProperty(QTextListFormat::ListStyle), (int) KoListStyle::Bullet); block = block.next(); QVERIFY(block.isValid()); style.applyStyle(block); // make this a listStyle m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); block = m_doc->begin().next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 2 KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); qreal counterSpacing = data->counterSpacing(); QVERIFY(counterSpacing > 0.); // 12 is hardcoded to be the width of a discitem (taken from the default font): QCOMPARE(blockLayout->lineAt(0).x(), 12.0 + counterSpacing); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 3 QCOMPARE(blockLayout->lineAt(0).x(), 12.0 + counterSpacing); QVERIFY(blockLayout->lineCount() > 1); QCOMPARE(blockLayout->lineAt(1).x(), 12.0 + counterSpacing); // make sure not only the first line is indented block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); // parag 4 QCOMPARE(blockLayout->lineAt(0).x(), 0.0); }
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; }
void TestSections::testBasicLayout() { KSectionStyle *sectionStyle = new KSectionStyle(); Q_ASSERT(sectionStyle); sectionStyle->setLeftMargin(0.0); sectionStyle->setRightMargin(0.0); initTest(sectionStyle); m_layout->layout(); // a block in a section with no special margins or columns should be just as wide // as the reference (first) block. QTextLayout *blockLayout = m_doc->begin().layout(); QCOMPARE(blockLayout->lineAt(0).width(), 200.0); blockLayout = m_doc->begin().next().layout(); QCOMPARE(blockLayout->lineAt(0).width(), 200.0); cleanupTest(); }
void TestSections::testMoveByMargin() { KSectionStyle *sectionStyle = new KSectionStyle(); Q_ASSERT(sectionStyle); sectionStyle->setLeftMargin(20.0); sectionStyle->setRightMargin(-20.0); initTest(sectionStyle); m_layout->layout(); // a block in a section with margins (and no columns) that essentially just moves the block // should be just as wide as the reference (first) block. QTextLayout *blockLayout = m_doc->begin().layout(); QCOMPARE(blockLayout->lineAt(0).width(), 200.0); blockLayout = m_doc->begin().next().layout(); QCOMPARE(blockLayout->lineAt(0).width(), 200.0); cleanupTest(); }
ParagraphFragment::ParagraphFragment(KShape *shape, const QTextBlock &textBlock, KParagraphStyle *style) : m_shape(shape) { QTextLayout *layout = textBlock.layout(); m_isSingleLine = (layout->lineCount() == 1); // border rectangle left and right m_border.setLeft(0.0); m_border.setRight(shape->size().width()); // first line rectangle m_firstLine = layout->lineAt(0).rect(); m_firstLine.setRight(m_border.right() - style->rightMargin()); // counter rectangle KTextBlockData *blockData = static_cast<KTextBlockData*>(textBlock.userData()); if (blockData != NULL) { m_counter = QRectF(blockData->counterPosition(), QSizeF(blockData->counterWidth() - blockData->counterSpacing(), m_firstLine.height())); } // following lines rectangle if (!m_isSingleLine) { m_followingLines = QRectF(layout->lineAt(1).rect().topLeft(), layout->lineAt(layout->lineCount() - 1).rect().bottomRight()); } else { m_followingLines = m_firstLine; } // border rectangle top and bottom m_border.setTop(m_firstLine.top() - style->topMargin()); m_border.setBottom(m_isSingleLine ? m_firstLine.bottom() + style->bottomMargin() : m_followingLines.bottom() + style->bottomMargin()); // TODO: the lines overlap slightly so right now we simply // calculate the mean of the two y-values, should be handled properly if (!m_isSingleLine) { qreal lineBreak((m_firstLine.bottom() + m_followingLines.top()) / 2.0); m_firstLine.setBottom(lineBreak); m_counter.setBottom(lineBreak); m_followingLines.setTop(lineBreak); } }
void TestSections::testShrinkByMargin() { KSectionStyle *sectionStyle = new KSectionStyle(); Q_ASSERT(sectionStyle); sectionStyle->setLeftMargin(20.0); sectionStyle->setRightMargin(20.0); initTest(sectionStyle); m_layout->layout(); // a block in a section with margins (and no columns) should be exact amoung less wide // as the reference (first) block. QTextLayout *blockLayout = m_doc->begin().layout(); QCOMPARE(blockLayout->lineAt(0).width(), 200.0); blockLayout = m_doc->begin().next().layout(); QEXPECT_FAIL("", "unimplemented", Abort); QCOMPARE(blockLayout->lineAt(0).width(), 160.0); cleanupTest(); }
qreal TextDocumentLayout::blockWidth(const QTextBlock &block) { QTextLayout *layout = block.layout(); if (!layout->lineCount()) return 0; // only for layouted blocks qreal blockWidth = 0; for (int i = 0; i < layout->lineCount(); ++i) { QTextLine line = layout->lineAt(i); blockWidth = qMax(line.naturalTextWidth() + 8, blockWidth); } return blockWidth; }
void TestTableLayout::testColumnWidthUndefined() { setupTest("","","","","",""); m_layout->layout(); QCOMPARE(m_table->columns(), 3); QCOMPARE(m_table->rows(), 3); QVERIFY(qAbs(m_block.layout()->lineAt(0).width() - 200.0) < ROUNDING); QTextLayout *lay = bottomLeftCellBlock().layout(); QVERIFY(lay); QCOMPARE(lay->lineCount(), 1); QVERIFY(qAbs(lay->lineAt(0).width() - 200.0/3) < ROUNDING); lay = bottomMidCellBlock().layout(); QVERIFY(lay); QCOMPARE(lay->lineCount(), 1); QVERIFY(qAbs(lay->lineAt(0).width() - 200.0/3) < ROUNDING); lay = bottomRightCellBlock().layout(); QVERIFY(lay); QCOMPARE(lay->lineCount(), 1); QVERIFY(qAbs(lay->lineAt(0).width() - 200.0/3) < ROUNDING); }
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; }
QRectF TextDocumentLayout::blockBoundingRect(const QTextBlock &block) const { if (!block.isValid()) { return QRectF(); } QTextLayout *tl = block.layout(); if (!tl->lineCount()) const_cast<TextDocumentLayout*>(this)->layoutBlock(block); QRectF br; if (block.isVisible()) { br = QRectF(QPointF(0, 0), tl->boundingRect().bottomRight()); if (tl->lineCount() == 1) br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth())); qreal margin = document()->documentMargin(); br.adjust(0, 0, margin, 0); if (!block.next().isValid()) br.adjust(0, 0, 0, margin); } return br; }
void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QTextBlock &block, const QPointF &position, const QColor &textColor, const QColor &anchorColor, int selectionStart, int selectionEnd) { Q_ASSERT(textDocument); #ifndef QT_NO_IM int preeditLength = block.isValid() ? block.layout()->preeditAreaText().length() : 0; int preeditPosition = block.isValid() ? block.layout()->preeditAreaPosition() : -1; #endif QVarLengthArray<QTextLayout::FormatRange> colorChanges; mergeFormats(block.layout(), &colorChanges); QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft() + position; if (QTextList *textList = block.textList()) { QPointF pos = blockPosition; QTextLayout *layout = block.layout(); if (layout->lineCount() > 0) { QTextLine firstLine = layout->lineAt(0); Q_ASSERT(firstLine.isValid()); setCurrentLine(firstLine); QRectF textRect = firstLine.naturalTextRect(); pos += textRect.topLeft(); if (block.textDirection() == Qt::RightToLeft) pos.rx() += textRect.width(); const QTextCharFormat charFormat = block.charFormat(); QFont font(charFormat.font()); QFontMetricsF fontMetrics(font); QTextListFormat listFormat = textList->format(); QString listItemBullet; switch (listFormat.style()) { case QTextListFormat::ListCircle: listItemBullet = QChar(0x25E6); // White bullet break; case QTextListFormat::ListSquare: listItemBullet = QChar(0x25AA); // Black small square break; case QTextListFormat::ListDecimal: case QTextListFormat::ListLowerAlpha: case QTextListFormat::ListUpperAlpha: case QTextListFormat::ListLowerRoman: case QTextListFormat::ListUpperRoman: listItemBullet = textList->itemText(block); break; default: listItemBullet = QChar(0x2022); // Black bullet break; }; QSizeF size(fontMetrics.width(listItemBullet), fontMetrics.height()); qreal xoff = fontMetrics.width(QLatin1Char(' ')); if (block.textDirection() == Qt::LeftToRight) xoff = -xoff - size.width(); setPosition(pos + QPointF(xoff, 0)); QTextLayout layout; layout.setFont(font); layout.setText(listItemBullet); // Bullet layout.beginLayout(); QTextLine line = layout.createLine(); line.setPosition(QPointF(0, 0)); layout.endLayout(); QList<QGlyphRun> glyphRuns = layout.glyphRuns(); for (int i=0; i<glyphRuns.size(); ++i) addUnselectedGlyphs(glyphRuns.at(i)); } } int textPos = block.position(); QTextBlock::iterator blockIterator = block.begin(); while (!blockIterator.atEnd()) { QTextFragment fragment = blockIterator.fragment(); QString text = fragment.text(); if (text.isEmpty()) continue; QTextCharFormat charFormat = fragment.charFormat(); QFont font(charFormat.font()); QFontMetricsF fontMetrics(font); int fontHeight = fontMetrics.descent() + fontMetrics.ascent(); int valign = charFormat.verticalAlignment(); if (valign == QTextCharFormat::AlignSuperScript) setPosition(QPointF(blockPosition.x(), blockPosition.y() - fontHeight / 2)); else if (valign == QTextCharFormat::AlignSubScript) setPosition(QPointF(blockPosition.x(), blockPosition.y() + fontHeight / 6)); else setPosition(blockPosition); if (text.contains(QChar::ObjectReplacementCharacter)) { QTextFrame *frame = qobject_cast<QTextFrame *>(textDocument->objectForFormat(charFormat)); if (frame && frame->frameFormat().position() == QTextFrameFormat::InFlow) { int blockRelativePosition = textPos - block.position(); QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition); if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); } QQuickTextNodeEngine::SelectionState selectionState = (selectionStart < textPos + text.length() && selectionEnd >= textPos) ? QQuickTextNodeEngine::Selected : QQuickTextNodeEngine::Unselected; addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos); } textPos += text.length(); } else { if (charFormat.foreground().style() != Qt::NoBrush) setTextColor(charFormat.foreground().color()); else if (charFormat.isAnchor()) setTextColor(anchorColor); else setTextColor(textColor); int fragmentEnd = textPos + fragment.length(); #ifndef QT_NO_IM if (preeditPosition >= 0 && (preeditPosition + block.position()) >= textPos && (preeditPosition + block.position()) <= fragmentEnd) { fragmentEnd += preeditLength; } #endif if (charFormat.background().style() != Qt::NoBrush) { QTextLayout::FormatRange additionalFormat; additionalFormat.start = textPos - block.position(); additionalFormat.length = fragmentEnd - textPos; additionalFormat.format = charFormat; colorChanges << additionalFormat; } textPos = addText(block, charFormat, textColor, colorChanges, textPos, fragmentEnd, selectionStart, selectionEnd); } ++blockIterator; } #ifndef QT_NO_IM if (preeditLength >= 0 && textPos <= block.position() + preeditPosition) { setPosition(blockPosition); textPos = block.position() + preeditPosition; QTextLine line = block.layout()->lineForTextPosition(preeditPosition); if (!currentLine().isValid() || line.lineNumber() != currentLine().lineNumber()) { setCurrentLine(line); } textPos = addText(block, block.charFormat(), textColor, colorChanges, textPos, textPos + preeditLength, selectionStart, selectionEnd); } #endif setCurrentLine(QTextLine()); // Reset current line because the text layout changed m_hasContents = true; }
void TextLayer::recreateTexture(VidgfxContext *gfx) { if(!m_isTexDirty) return; // Don't waste any time if it hasn't changed m_isTexDirty = false; // Delete existing texture if one exists if(m_texture != NULL) vidgfx_context_destroy_tex(gfx, m_texture); m_texture = NULL; // Determine texture size. We need to keep in mind that the text in the // document might extend outside of the layer's bounds. m_document.setTextWidth(m_rect.width()); QSize size( (int)ceilf(m_document.size().width()), (int)ceilf(m_document.size().height())); if(m_document.isEmpty() || size.isEmpty()) { // Nothing to display return; } // Create temporary canvas. We need to be careful here as text is rendered // differently on premultiplied vs non-premultiplied pixel formats. On a // premultiplied format text is rendered with subpixel rendering enabled // while on a non-premultiplied format it is not. As we don't want subpixel // rendering we use the standard ARGB32 format. QSize imgSize( size.width() + m_strokeSize * 2, size.height() + m_strokeSize * 2); QImage img(imgSize, QImage::Format_ARGB32); img.fill(Qt::transparent); QPainter p(&img); p.setRenderHint(QPainter::Antialiasing, true); // Render text //m_document.drawContents(&p); // Render stroke if(m_strokeSize > 0) { #define STROKE_TECHNIQUE 0 #if STROKE_TECHNIQUE == 0 // Technique 0: Use QTextDocument's built-in text outliner //quint64 timeStart = App->getUsecSinceExec(); QTextDocument *outlineDoc = m_document.clone(this); QTextCharFormat format; QPen pen(m_strokeColor, (double)(m_strokeSize * 2)); pen.setJoinStyle(Qt::RoundJoin); format.setTextOutline(pen); QTextCursor cursor(outlineDoc); cursor.select(QTextCursor::Document); cursor.mergeCharFormat(format); // Take into account the stroke offset p.translate(m_strokeSize, m_strokeSize); //quint64 timePath = App->getUsecSinceExec(); outlineDoc->drawContents(&p); delete outlineDoc; //quint64 timeEnd = App->getUsecSinceExec(); //appLog() << "Path time = " << (timePath - timeStart) << " usec"; //appLog() << "Render time = " << (timeEnd - timePath) << " usec"; //appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 1 // Technique 1: Create a text QPainterPath and stroke it quint64 timeStart = App->getUsecSinceExec(); // Create the path for the text's stroke QPainterPath path; QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QPointF pos = layout->position() + line.position(); pos.ry() += line.ascent(); //appLog() << pos << ": " << text; path.addText(pos, block.charFormat().font(), text); } block = block.next(); } quint64 timePath = App->getUsecSinceExec(); path = path.simplified(); // Fixes gaps with large stroke sizes quint64 timeSimplify = App->getUsecSinceExec(); // Render the path //p.strokePath(path, QPen(m_strokeColor, m_strokeSize)); // Convert it to a stroke QPainterPathStroker stroker; stroker.setWidth(m_strokeSize); //stroker.setCurveThreshold(2.0); stroker.setJoinStyle(Qt::RoundJoin); path = stroker.createStroke(path); // Render the path p.fillPath(path, m_strokeColor); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Path time = " << (timePath - timeStart) << " usec"; appLog() << "Simplify time = " << (timeSimplify - timePath) << " usec"; appLog() << "Render time = " << (timeEnd - timeSimplify) << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 2 // Technique 2: Similar to technique 1 but do each block separately quint64 timeStart = App->getUsecSinceExec(); quint64 timeTotalSimplify = 0; quint64 timeTotalRender = 0; // Create the path for the text's stroke QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { // Convert this block to a painter path QPainterPath path; QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QPointF pos = layout->position() + line.position() + QPointF(m_strokeSize, m_strokeSize); pos.ry() += line.ascent(); //appLog() << pos << ": " << text; path.addText(pos, block.charFormat().font(), text); } // Prevent gaps appearing at larger stroke sizes quint64 timeA = App->getUsecSinceExec(); path = path.simplified(); quint64 timeB = App->getUsecSinceExec(); timeTotalSimplify += timeB - timeA; // Render the path QPen pen(m_strokeColor, m_strokeSize * 2); pen.setJoinStyle(Qt::RoundJoin); p.strokePath(path, pen); timeA = App->getUsecSinceExec(); timeTotalRender += timeA - timeB; // Iterate block = block.next(); } // Make the final draw take into account the stroke offset p.translate(m_strokeSize, m_strokeSize); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Simplify time = " << timeTotalSimplify << " usec"; appLog() << "Render time = " << timeTotalRender << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #elif STROKE_TECHNIQUE == 3 // Technique 3: Raster brute-force where for each destination pixel // we measure the distance to the closest opaque source pixel quint64 timeStart = App->getUsecSinceExec(); // Get bounding region based on text line bounding rects QRegion region; QTextBlock &block = m_document.firstBlock(); int numBlocks = m_document.blockCount(); for(int i = 0; i < numBlocks; i++) { QTextLayout *layout = block.layout(); for(int j = 0; j < layout->lineCount(); j++) { QTextLine &line = layout->lineAt(j); const QString text = block.text().mid( line.textStart(), line.textLength()); QRect rect = line.naturalTextRect() .translated(layout->position()).toAlignedRect(); if(rect.isEmpty()) continue; // Don't add empty rectangles rect.adjust(0, 0, 1, 0); // QTextLine is incorrect? rect.adjust( -m_strokeSize, -m_strokeSize, m_strokeSize, m_strokeSize); //appLog() << rect; region += rect; } // Iterate block = block.next(); } quint64 timeRegion = App->getUsecSinceExec(); #if 0 // Debug bounding region QPainterPath regionPath; regionPath.addRegion(region); regionPath.setFillRule(Qt::WindingFill); p.fillPath(regionPath, QColor(255, 0, 0, 128)); #endif // 0 // We cannot read and write to the same image at the same time so // create a second one. Note that this is not premultiplied. QImage imgOut(size, QImage::Format_ARGB32); imgOut.fill(Qt::transparent); // Do distance calculation. We assume that non-fully transparent // pixels are always next to a fully opaque one so if the closest // "covered" pixel is not fully opaque then we can use that pixel's // opacity to determine the distance to the shape's edge. for(int y = 0; y < img.height(); y++) { for(int x = 0; x < img.width(); x++) { if(!region.contains(QPoint(x, y))) continue; float dist = getDistance(img, x, y, m_strokeSize); // We fake antialiasing by blurring the edge by 1px float outEdge = (float)m_strokeSize; if(dist >= outEdge) continue; // Outside stroke completely float opacity = qMin(1.0f, outEdge - dist); QColor col = m_strokeColor; col.setAlphaF(col.alphaF() * opacity); // Blend the stroke so that it appears under the existing // pixel data QRgb origRgb = img.pixel(x, y); QColor origCol(origRgb); origCol.setAlpha(qAlpha(origRgb)); col = blendColors(col, origCol, 1.0f); imgOut.setPixel(x, y, col.rgba()); } } quint64 timeRender = App->getUsecSinceExec(); // Swap image data p.end(); img = imgOut; p.begin(&img); quint64 timeEnd = App->getUsecSinceExec(); appLog() << "Region time = " << (timeRegion - timeStart) << " usec"; appLog() << "Render time = " << (timeRender - timeRegion) << " usec"; appLog() << "Swap time = " << (timeEnd - timeRender) << " usec"; appLog() << "Full time = " << (timeEnd - timeStart) << " usec"; #endif // STROKE_TECHNIQUE } // Render text m_document.drawContents(&p); // Convert the image to a GPU texture m_texture = vidgfx_context_new_tex(gfx, img); // Preview texture for debugging //img.save(App->getDataDirectory().filePath("Preview.png")); }
void TestDocumentLayout::testNumberedList() { initForNewTest("Base\nListItem1\nListItem2\nListItem3\nListItem4\nListItem5\nListItem6\nListItem6\nListItem7\nListItem8\nListItem9\nListItem10\nListItem11\nListItem12\n"); KoParagraphStyle style; m_styleManager->add(&style); QTextBlock block = m_doc->begin(); style.applyStyle(block); block = block.next(); KoListStyle listStyle; KoListLevelProperties llp; llp.setStyle(KoListStyle::DecimalItem); listStyle.setLevelProperties(llp); style.setListStyle(&listStyle); QTextList *previous = 0; int i; for (i = 1; i <= 9; i++) { QVERIFY(block.isValid()); // qDebug() << "->" << block.text(); style.applyStyle(block); QTextList *textList = block.textList(); QVERIFY(textList); if (previous == 0) { previous = textList; } else { QCOMPARE(textList, previous); } QCOMPARE(textList->format().intProperty(QTextListFormat::ListStyle), (int)(KoListStyle::DecimalItem)); block = block.next(); } m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); QTextBlock blok = m_doc->begin().next(); qreal indent = blok.layout()->lineAt(0).x(); QVERIFY(indent > 0.0); for (i = 1; i <= 9; ++i) { // qDebug() << "=>" << blok.text(); QTextList *textList = blok.textList(); QVERIFY(textList); QCOMPARE(blok.layout()->lineAt(0).x(), indent); // all the same indent. blok = blok.next(); } // now make number of listitems be more than 10, so we use 2 digits. for (i = 9; i <= 12; ++i) { QVERIFY(block.isValid()); style.applyStyle(block); // qDebug() << "->" << block.text(); block = block.next(); } m_layout->layout(); blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0); blok = m_doc->begin().next(); qreal indent2 = blok.layout()->lineAt(0).x(); QVERIFY(indent2 > indent); // since it takes an extra digit for (i = 2; i <= 12; ++i) { // qDebug() << "=>" << blok.text(); QCOMPARE(blok.layout()->lineAt(0).x(), indent2); // all the same indent. blok = blok.next(); } // now to make sure the text is actually properly set. block = m_doc->begin().next(); i = 1; while (block.isValid() && i < 13) { KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QCOMPARE(data->counterText(), QString::number(i++)); block = block.next(); } llp.setListItemSuffix("."); llp.setStartValue(4); listStyle.setLevelProperties(llp); QTextCursor cursor(m_doc); cursor.setPosition(10); // listItem1 QTextBlockFormat format = cursor.blockFormat(); format.setProperty(KoParagraphStyle::ListStartValue, 4); cursor.setBlockFormat(format); cursor.setPosition(40); // listItem4 format = cursor.blockFormat(); format.setProperty(KoParagraphStyle::ListStartValue, 12); cursor.setBlockFormat(format); // at this point we start numbering at 4. Have 4, 5, 6, 12, 13, 14, 15 etc m_layout->layout(); // now to make sur the text is actually properly set. block = m_doc->begin().next(); i = 4; while (block.isValid() && i < 22) { if (i == 7) { i = 12; } KoTextBlockData *data = dynamic_cast<KoTextBlockData *>(block.userData()); QVERIFY(data); QCOMPARE(data->counterText(), QString::number(i++)); block = block.next(); } }
QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect &clip) { if (begin.isNull() || end.isNull() || begin.position() > end.position()) return QPainterPath(); QPointF offset = m_editor->contentOffset(); QRect viewportRect = rect(); QTextDocument *document = m_editor->document(); if (m_editor->blockBoundingGeometry(begin.block()).translated(offset).top() > clip.bottom() + 10 || m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() < clip.top() - 10 ) return QPainterPath(); // nothing of the selection is visible QTextBlock block = begin.block(); if (block.blockNumber() < m_editor->firstVisibleBlock().blockNumber() - 4) block = m_editor->document()->findBlockByNumber(m_editor->firstVisibleBlock().blockNumber() - 4); bool inSelection = false; QVector<QRectF> selection; if (begin.position() == end.position()) { // special case empty selections const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); QTextLayout *blockLayout = block.layout(); int pos = begin.position() - begin.block().position(); QTextLine line = blockLayout->lineForTextPosition(pos); QRectF lineRect = line.naturalTextRect(); int x = line.cursorToX(pos); lineRect.setLeft(x - m_borderWidth); lineRect.setRight(x + m_borderWidth); selection += lineRect.translated(blockGeometry.topLeft()); } else { for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) { if (! block.isVisible()) continue; const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); QTextLayout *blockLayout = block.layout(); QTextLine line = blockLayout->lineAt(0); bool firstOrLastBlock = false; int beginChar = 0; if (!inSelection) { if (block == begin.block()) { beginChar = begin.positionInBlock(); line = blockLayout->lineForTextPosition(beginChar); firstOrLastBlock = true; } inSelection = true; } else { // while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace()) // ++beginChar; // if (beginChar == block.length()) // beginChar = 0; } int lastLine = blockLayout->lineCount()-1; int endChar = -1; if (block == end.block()) { endChar = end.positionInBlock(); lastLine = blockLayout->lineForTextPosition(endChar).lineNumber(); inSelection = false; firstOrLastBlock = true; } else { endChar = block.length(); while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace()) --endChar; } QRectF lineRect = line.naturalTextRect(); if (beginChar < endChar) { lineRect.setLeft(line.cursorToX(beginChar)); if (line.lineNumber() == lastLine) lineRect.setRight(line.cursorToX(endChar)); selection += lineRect.translated(blockGeometry.topLeft()); for (int lineIndex = line.lineNumber()+1; lineIndex <= lastLine; ++lineIndex) { line = blockLayout->lineAt(lineIndex); lineRect = line.naturalTextRect(); if (lineIndex == lastLine) lineRect.setRight(line.cursorToX(endChar)); selection += lineRect.translated(blockGeometry.topLeft()); } } else { // empty lines const int emptyLineSelectionSize = 16; if (!firstOrLastBlock && !selection.isEmpty()) { // middle lineRect.setLeft(selection.last().left()); } else if (inSelection) { // first line lineRect.setLeft(line.cursorToX(beginChar)); } else { // last line if (endChar == 0) break; lineRect.setLeft(line.cursorToX(endChar) - emptyLineSelectionSize); } lineRect.setRight(lineRect.left() + emptyLineSelectionSize); selection += lineRect.translated(blockGeometry.topLeft()); } if (!inSelection) break; if (blockGeometry.translated(offset).y() > 2*viewportRect.height()) break; } } if (selection.isEmpty()) return QPainterPath(); QVector<QPointF> points; const int margin = m_borderWidth/2; const int extra = 0; const QRectF &firstSelection = selection.at(0); points += (firstSelection.topLeft() + firstSelection.topRight()) / 2 + QPointF(0, -margin); points += firstSelection.topRight() + QPointF(margin+1, -margin); points += firstSelection.bottomRight() + QPointF(margin+1, 0); const int count = selection.count(); for (int i = 1; i < count-1; ++i) { #define MAX3(a,b,c) qMax(a, qMax(b,c)) qreal x = MAX3(selection.at(i-1).right(), selection.at(i).right(), selection.at(i+1).right()) + margin; points += QPointF(x+1, selection.at(i).top()); points += QPointF(x+1, selection.at(i).bottom()); } const QRectF &lastSelection = selection.at(count-1); points += lastSelection.topRight() + QPointF(margin+1, 0); points += lastSelection.bottomRight() + QPointF(margin+1, margin+extra); points += lastSelection.bottomLeft() + QPointF(-margin, margin+extra); points += lastSelection.topLeft() + QPointF(-margin, 0); for (int i = count-2; i > 0; --i) { #define MIN3(a,b,c) qMin(a, qMin(b,c)) qreal x = MIN3(selection.at(i-1).left(), selection.at(i).left(), selection.at(i+1).left()) - margin; points += QPointF(x, selection.at(i).bottom()+extra); points += QPointF(x, selection.at(i).top()); } points += firstSelection.bottomLeft() + QPointF(-margin, extra); points += firstSelection.topLeft() + QPointF(-margin, -margin); QPainterPath path; const int corner = 4; path.moveTo(points.at(0)); points += points.at(0); QPointF previous = points.at(0); for (int i = 1; i < points.size(); ++i) { QPointF point = points.at(i); if (point.y() == previous.y() && qAbs(point.x() - previous.x()) > 2*corner) { QPointF tmp = QPointF(previous.x() + corner * ((point.x() > previous.x())?1:-1), previous.y()); path.quadTo(previous, tmp); previous = tmp; i--; continue; } else if (point.x() == previous.x() && qAbs(point.y() - previous.y()) > 2*corner) { QPointF tmp = QPointF(previous.x(), previous.y() + corner * ((point.y() > previous.y())?1:-1)); path.quadTo(previous, tmp); previous = tmp; i--; continue; } QPointF target = (previous + point) / 2; path.quadTo(previous, target); previous = points.at(i); } path.closeSubpath(); path.translate(offset); return path; }
void Text::layout() { #if 0 QSizeF pageSize(-1.0, 1000000); setPos(0.0, 0.0); if (parent() && _layoutToParentWidth) { pageSize.setWidth(parent()->width()); if (parent()->type() == HBOX || parent()->type() == VBOX || parent()->type() == TBOX) { Box* box = static_cast<Box*>(parent()); rxpos() += box->leftMargin() * DPMM; rypos() += box->topMargin() * DPMM; // pageSize.setHeight(box->height() - (box->topMargin() + box->bottomMargin()) * DPMM); pageSize.setWidth(box->width() - (box->leftMargin() + box->rightMargin()) * DPMM); } } QTextOption to = _doc->defaultTextOption(); to.setUseDesignMetrics(true); to.setWrapMode(pageSize.width() <= 0.0 ? QTextOption::NoWrap : QTextOption::WrapAtWordBoundaryOrAnywhere); _doc->setDefaultTextOption(to); if (pageSize.width() <= 0.0) _doc->setTextWidth(_doc->idealWidth()); else _doc->setPageSize(pageSize); if (hasFrame()) { frame = QRectF(); 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) // frame |= tl->lineAt(0).naturalTextRect().translated(tl->position()); frame |= tl->lineAt(0).rect().translated(tl->position()); } if (circle()) { if (frame.width() > frame.height()) { frame.setY(frame.y() + (frame.width() - frame.height()) * -.5); frame.setHeight(frame.width()); } else { frame.setX(frame.x() + (frame.height() - frame.width()) * -.5); frame.setWidth(frame.height()); } } qreal w = (paddingWidth() + frameWidth() * .5) * DPMM; frame.adjust(-w, -w, w, w); w = frameWidth() * DPMM; _bbox = frame.adjusted(-w, -w, w, w); } else { _bbox = QRectF(QPointF(0.0, 0.0), _doc->size()); //_doc->documentLayout()->frameBoundingRect(_doc->rootFrame()); } _doc->setModified(false); style().layout(this); // process alignment if ((style().align() & ALIGN_VCENTER) && (subtype() == TEXT_TEXTLINE)) { // special case: vertically centered text with TextLine needs to // take into account the line width TextLineSegment* tls = (TextLineSegment*)parent(); TextLine* tl = (TextLine*)(tls->line()); qreal textlineLineWidth = point(tl->lineWidth()); rypos() -= textlineLineWidth * .5; } if (parent() == 0) return; if (parent()->type() == SEGMENT) { Segment* s = static_cast<Segment*>(parent()); rypos() += s ? s->measure()->system()->staff(staffIdx())->y() : 0.0; } #endif Font f = style().font(spatium()); qreal asc, desc, leading; qreal w = textMetrics(f.family(), _text, f.size(), &asc, &desc, &leading); // printf("text(%s) asc %f desc %f leading %f w %f\n", qPrintable(_text), asc, desc, leading, w); _lineHeight = asc + desc; _lineSpacing = _lineHeight + leading; _baseLine = asc; setbbox(QRectF(0.0, -asc, w, _lineHeight)); #if 0 if (parent() && _layoutToParentWidth) { qreal wi = parent()->width(); qreal ph = parent()->height(); qreal x; qreal y = pos.y(); if (align() & ALIGN_HCENTER) x = (wi - w) * .5; else if (align() & ALIGN_RIGHT) x = wi - w; else x = 0.0; if (align() & ALIGN_VCENTER) y = (ph - asc) * .5; else if (align() & ALIGN_BOTTOM) y = ph - asc; else y = asc; setPos(x, y + asc); } #endif style().layout(this); // process alignment rypos() += asc; if (parent() && _layoutToParentWidth) { qreal wi = parent()->width(); if (align() & ALIGN_HCENTER) rxpos() = (wi - w) * .5; else if (align() & ALIGN_RIGHT) rxpos() = wi - w; } if ((style().align() & ALIGN_VCENTER) && (subtype() == TEXT_TEXTLINE)) { // special case: vertically centered text with TextLine needs to // take into account the line width TextLineSegment* tls = (TextLineSegment*)parent(); TextLine* tl = (TextLine*)(tls->line()); qreal textlineLineWidth = point(tl->lineWidth()); rypos() -= textlineLineWidth * .5; } if (parent() == 0) return; if (parent()->type() == SEGMENT) { Segment* s = static_cast<Segment*>(parent()); rypos() += s ? s->measure()->system()->staff(staffIdx())->y() : 0.0; } }
void ListViewDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItemV4 opt = option; initStyleOption ( &opt, index ); painter->save(); painter->setClipRect ( opt.rect ); opt.features |= QStyleOptionViewItem::WrapText; opt.text = index.data().toString(); opt.textElideMode = Qt::ElideRight; opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); //const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); const int iconSize = 48; QRect iconbox = opt.rect; const int textMargin = style->pixelMetric ( QStyle::PM_FocusFrameHMargin, 0, opt.widget ) + 1; QRect textRect = opt.rect; QRect textHighlightRect = textRect; // clip the decoration on top, remove width padding textRect.adjust ( textMargin,iconSize + textMargin + 5,-textMargin,0 ); textHighlightRect.adjust ( 0,iconSize + 5,0,0 ); // draw background { QSize textSize = viewItemTextSize ( &opt ); QPalette::ColorGroup cg; QStyleOptionViewItemV4 opt2(opt); if((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) { if(! ( opt.state & QStyle::State_Active )) cg = QPalette::Inactive; else cg = QPalette::Normal; } else { cg = QPalette::Disabled; } opt2.palette.setCurrentColorGroup(cg); // fill in background, if any if ( opt.backgroundBrush.style() != Qt::NoBrush ) { QPointF oldBO = painter->brushOrigin(); painter->setBrushOrigin ( opt.rect.topLeft() ); painter->fillRect ( opt.rect, opt.backgroundBrush ); painter->setBrushOrigin ( oldBO ); } if ( opt.showDecorationSelected ) { drawSelectionRect(painter,opt2, opt.rect); drawFocusRect(painter,opt2, opt.rect); //painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); } else { //if ( opt.state & QStyle::State_Selected ) { //QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, opt.widget ); //painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, QPalette::Highlight ) ); drawSelectionRect(painter,opt2, textHighlightRect); drawFocusRect(painter,opt2, textHighlightRect); } } } // draw the icon { QIcon::Mode mode = QIcon::Normal; if ( ! ( opt.state & QStyle::State_Enabled ) ) mode = QIcon::Disabled; else if ( opt.state & QStyle::State_Selected ) mode = QIcon::Selected; QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; iconbox.setHeight ( iconSize ); opt.icon.paint ( painter, iconbox, Qt::AlignCenter, mode, state ); } // set the text colors QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if ( cg == QPalette::Normal && ! ( opt.state & QStyle::State_Active ) ) cg = QPalette::Inactive; if ( opt.state & QStyle::State_Selected ) { painter->setPen ( opt.palette.color ( cg, QPalette::HighlightedText ) ); } else { painter->setPen ( opt.palette.color ( cg, QPalette::Text ) ); } // draw the text QTextOption textOption; textOption.setWrapMode ( QTextOption::WrapAtWordBoundaryOrAnywhere ); textOption.setTextDirection ( opt.direction ); textOption.setAlignment ( QStyle::visualAlignment ( opt.direction, opt.displayAlignment ) ); QTextLayout textLayout; textLayout.setTextOption ( textOption ); textLayout.setFont ( opt.font ); textLayout.setText ( opt.text ); qreal width, height; viewItemTextLayout ( textLayout, iconbox.width(), height, width ); const int lineCount = textLayout.lineCount(); const QRect layoutRect = QStyle::alignedRect ( opt.direction, opt.displayAlignment, QSize ( iconbox.width(), int ( height ) ), textRect ); const QPointF position = layoutRect.topLeft(); for ( int i = 0; i < lineCount; ++i ) { const QTextLine line = textLayout.lineAt ( i ); line.draw ( painter, position ); } painter->restore(); }
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); }