From: Martin Jones Date: Mon, 28 Nov 2011 05:32:31 +0000 (+1000) Subject: Fix multiline eliding and support eliding when height is set. X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=7d4a187bc7537b1f6d441aabc1bbcffafd7017e7;p=konrad%2Fqtdeclarative.git Fix multiline eliding and support eliding when height is set. Task-number: QTBUG-22920, QTBUG-22116 Change-Id: Ibe78ce1b0b438eec32955b986a8740f173cd082f Reviewed-by: Yann Bodson --- diff --git a/src/declarative/items/qquicktext.cpp b/src/declarative/items/qquicktext.cpp index e11e4be..e3286f0 100644 --- a/src/declarative/items/qquicktext.cpp +++ b/src/declarative/items/qquicktext.cpp @@ -106,7 +106,7 @@ QQuickTextPrivate::QQuickTextPrivate() richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false), layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true), - naturalWidth(0), doc(0), textLine(0), nodeType(NodeIsNull) + naturalWidth(0), doc(0), elipsisLayout(0), textLine(0), nodeType(NodeIsNull) #if defined(Q_OS_MAC) , layoutThread(0), paintingThread(0) @@ -202,6 +202,7 @@ QSet QQuickTextDocumentWithImageResources::errors; QQuickTextPrivate::~QQuickTextPrivate() { + delete elipsisLayout; delete textLine; textLine = 0; } @@ -224,17 +225,21 @@ void QQuickTextPrivate::updateLayout() updateOnComponentComplete = true; return; } - + updateOnComponentComplete = false; layoutTextElided = false; // Setup instance of QTextLayout for all cases other than richtext if (!richText) { + if (elipsisLayout) { + delete elipsisLayout; + elipsisLayout = 0; + } layout.clearLayout(); layout.setFont(font); if (!styledText) { QString tmp = text; tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); singleline = !tmp.contains(QChar::LineSeparator); - if (singleline && !maximumLineCountValid && elideMode != QQuickText::ElideNone && q->widthValid()) { + if (singleline && !maximumLineCountValid && elideMode != QQuickText::ElideNone && q->widthValid() && wrapMode == QQuickText::NoWrap) { QFontMetrics fm(font); tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); if (tmp != text) { @@ -516,9 +521,6 @@ QRect QQuickTextPrivate::setupTextLayout() textOption.setUseDesignMetrics(true); layout.setTextOption(textOption); - bool elideText = false; - bool truncate = false; - QFontMetrics fm(layout.font()); elidePos = QPointF(); @@ -548,86 +550,87 @@ QRect QQuickTextPrivate::setupTextLayout() } qreal height = 0; + QRectF br; + + bool truncate = false; bool customLayout = isLineLaidOutConnected(); + bool elideEnabled = elideMode == QQuickText::ElideRight && q->widthValid(); + + layout.beginLayout(); + if (!lineWidth) + lineWidth = INT_MAX; + int linesLeft = maximumLineCount; + int visibleTextLength = 0; + forever { + QTextLine line = layout.createLine(); + if (!line.isValid()) + break; - if (maximumLineCountValid) { - layout.beginLayout(); - if (!lineWidth) - lineWidth = INT_MAX; - int linesLeft = maximumLineCount; - int visibleTextLength = 0; - while (linesLeft > 0) { - QTextLine line = layout.createLine(); - if (!line.isValid()) - break; + visibleCount++; - visibleCount++; + qreal preLayoutHeight = height; + if (customLayout) { + setupCustomLineGeometry(line, height); + } else if (lineWidth) { + line.setLineWidth(lineWidth); + line.setPosition(QPointF(line.position().x(), height)); + height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight; + } - if (customLayout) - setupCustomLineGeometry(line, height); - else if (lineWidth) - line.setLineWidth(lineWidth); + bool elide = false; + if (elideEnabled && q->heightValid() && height > q->height()) { + // This line does not fit in the remaining area. + elide = true; + if (visibleCount > 1) { + --visibleCount; + height = preLayoutHeight; + line.setLineWidth(0.0); + line.setPosition(QPointF(FLT_MAX,FLT_MAX)); + line = layout.lineAt(visibleCount-1); + } + } else { visibleTextLength += line.textLength(); + } - if (--linesLeft == 0) { - if (visibleTextLength < text.length()) { - truncate = true; - if (elideMode == QQuickText::ElideRight && q->widthValid()) { - qreal elideWidth = fm.width(elideChar); - // Need to correct for alignment - if (customLayout) - setupCustomLineGeometry(line, height, elideWidth); - else - line.setLineWidth(lineWidth - elideWidth); - if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) { - line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y())); - elidePos.setX(line.naturalTextRect().left() - elideWidth); - } else { - elidePos.setX(line.naturalTextRect().right()); - } - elideText = true; + if (elide || (maximumLineCountValid && --linesLeft == 0)) { + if (visibleTextLength < text.length()) { + truncate = true; + if (elideEnabled) { + qreal elideWidth = fm.width(elideChar); + // Need to correct for alignment + if (customLayout) + setupCustomLineGeometry(line, height, elideWidth); + else + line.setLineWidth(lineWidth - elideWidth); + if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) { + line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y())); + elidePos.setX(line.naturalTextRect().left() - elideWidth); + } else { + elidePos.setX(line.naturalTextRect().right()); } + elidePos.setY(line.position().y()); + if (!elipsisLayout) + elipsisLayout = new QTextLayout(elideChar, layout.font()); + elipsisLayout->beginLayout(); + QTextLine el = elipsisLayout->createLine(); + el.setPosition(elidePos); + elipsisLayout->endLayout(); + br = br.united(el.naturalTextRect()); } - } - } - layout.endLayout(); - - //Update truncated - if (truncated != truncate) { - truncated = truncate; - emit q->truncatedChanged(); - } - } else { - layout.beginLayout(); - forever { - QTextLine line = layout.createLine(); - if (!line.isValid()) + br = br.united(line.naturalTextRect()); break; - visibleCount++; - if (customLayout) - setupCustomLineGeometry(line, height); - else { - if (lineWidth) - line.setLineWidth(lineWidth); } } - layout.endLayout(); + br = br.united(line.naturalTextRect()); } + layout.endLayout(); - height = 0; - QRectF br; - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - // set line spacing - if (!customLayout) - line.setPosition(QPointF(line.position().x(), height)); - if (elideText && i == layout.lineCount()-1) { - elidePos.setY(height + fm.ascent()); - br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent()))); - } - br = br.united(line.naturalTextRect()); - height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight; + //Update truncated + if (truncated != truncate) { + truncated = truncate; + emit q->truncatedChanged(); } + if (!customLayout) br.setHeight(height); @@ -1295,7 +1298,7 @@ void QQuickText::resetHAlign() { Q_D(QQuickText); d->hAlignImplicit = true; - if (d->determineHorizontalAlignment() && isComponentComplete()) + if (isComponentComplete() && d->determineHorizontalAlignment()) d->updateLayout(); } @@ -1335,8 +1338,7 @@ bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAl bool QQuickTextPrivate::determineHorizontalAlignment() { - Q_Q(QQuickText); - if (hAlignImplicit && q->isComponentComplete()) { + if (hAlignImplicit) { bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft); } @@ -1553,15 +1555,17 @@ void QQuickText::setTextFormat(TextFormat format) d->richText = format == RichText; d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text)); - if (!wasRich && d->richText && isComponentComplete()) { - d->ensureDoc(); - d->doc->setText(d->text); - d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); - d->richTextAsImage = enableImageCache(); - } else { - d->rightToLeftText = d->text.isRightToLeft(); + if (isComponentComplete()) { + if (!wasRich && d->richText) { + d->ensureDoc(); + d->doc->setText(d->text); + d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); + d->richTextAsImage = enableImageCache(); + } else { + d->rightToLeftText = d->text.isRightToLeft(); + } + d->determineHorizontalAlignment(); } - d->determineHorizontalAlignment(); d->updateLayout(); emit textFormatChanged(d->format); @@ -1584,7 +1588,9 @@ void QQuickText::setTextFormat(TextFormat format) \endlist If this property is set to Text.ElideRight, it can be used with multiline - text. The text will only elide if maximumLineCount has been set. + text. The text will only elide if \c maximumLineCount, or \c height has been set. + If both \c maximumLineCount and \c height are set, \c maximumLineCount will + apply unless the lines do not fit in the height allowed. If the text is a multi-length string, and the mode is not \c Text.ElideNone, the first string that fits will be used, otherwise the last will be elided. @@ -1640,11 +1646,13 @@ QRectF QQuickText::boundingRect() const void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickText); - if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) + bool elide = d->elideMode != QQuickText::ElideNone && widthValid(); + if ((!d->internalWidthUpdate + && (newGeometry.width() != oldGeometry.width() || (elide && newGeometry.height() != oldGeometry.height()))) && (d->wrapMode != QQuickText::NoWrap || d->elideMode != QQuickText::ElideNone || d->hAlign != QQuickText::AlignLeft)) { - if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QQuickText::ElideNone && widthValid()) { + if ((d->singleline || d->maximumLineCountValid || heightValid()) && elide) { // We need to re-elide d->updateLayout(); } else { @@ -1731,6 +1739,8 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data } else { node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor); + if (d->elipsisLayout) + node->addTextLayout(QPoint(0, bounds.y()), d->elipsisLayout, d->color, d->style, d->styleColor); } return node; @@ -1842,9 +1852,7 @@ int QQuickText::resourcesLoading() const void QQuickText::componentComplete() { Q_D(QQuickText); - QQuickItem::componentComplete(); if (d->updateOnComponentComplete) { - d->updateOnComponentComplete = false; if (d->richText) { d->ensureDoc(); d->doc->setText(d->text); @@ -1854,8 +1862,10 @@ void QQuickText::componentComplete() d->rightToLeftText = d->text.isRightToLeft(); } d->determineHorizontalAlignment(); - d->updateLayout(); } + QQuickItem::componentComplete(); + if (d->updateOnComponentComplete) + d->updateLayout(); } diff --git a/src/declarative/items/qquicktext_p_p.h b/src/declarative/items/qquicktext_p_p.h index 16cc29a..2035f47 100644 --- a/src/declarative/items/qquicktext_p_p.h +++ b/src/declarative/items/qquicktext_p_p.h @@ -140,6 +140,7 @@ public: bool isLinkActivatedConnected(); QString anchorAt(const QPointF &pos); QTextLayout layout; + QTextLayout *elipsisLayout; QList linesRects; QQuickTextLine *textLine; diff --git a/tests/auto/declarative/qquicktext/data/multilineelide.qml b/tests/auto/declarative/qquicktext/data/multilineelide.qml new file mode 100644 index 0000000..23398a8 --- /dev/null +++ b/tests/auto/declarative/qquicktext/data/multilineelide.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +Text { + width: 200; height: 200 + wrapMode: Text.WordWrap + elide: Text.ElideRight + maximumLineCount: 3 + text: "the quick brown fox jumped over the lazy dog the quick brown fox jumped over the lazy dog" +} + diff --git a/tests/auto/declarative/qquicktext/tst_qquicktext.cpp b/tests/auto/declarative/qquicktext/tst_qquicktext.cpp index 88ac312..fb37357 100644 --- a/tests/auto/declarative/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/declarative/qquicktext/tst_qquicktext.cpp @@ -70,6 +70,7 @@ private slots: void width(); void wrap(); void elide(); + void multilineElide(); void textFormat(); void alignments_data(); @@ -448,6 +449,49 @@ void tst_qquicktext::elide() } } +void tst_qquicktext::multilineElide() +{ + QQuickView *canvas = createView(TESTDATA("multilineelide.qml")); + + QQuickText *myText = qobject_cast(canvas->rootObject()); + QVERIFY(myText != 0); + + QCOMPARE(myText->lineCount(), 3); + QCOMPARE(myText->truncated(), true); + + qreal lineHeight = myText->paintedHeight() / 3.; + + // reduce size and ensure fewer lines are drawn + myText->setHeight(lineHeight * 2); + QCOMPARE(myText->lineCount(), 2); + + myText->setHeight(lineHeight); + QCOMPARE(myText->lineCount(), 1); + + myText->setHeight(5); + QCOMPARE(myText->lineCount(), 1); + + myText->setHeight(lineHeight * 3); + QCOMPARE(myText->lineCount(), 3); + + // remove max count and show all lines. + myText->setHeight(1000); + myText->resetMaximumLineCount(); + + QCOMPARE(myText->truncated(), false); + + // reduce size again + myText->setHeight(lineHeight * 2); + QCOMPARE(myText->lineCount(), 2); + QCOMPARE(myText->truncated(), true); + + // change line height + myText->setLineHeight(1.1); + QCOMPARE(myText->lineCount(), 1); + + delete canvas; +} + void tst_qquicktext::textFormat() { {