Skip to content

Commit

Permalink
Fix multiline eliding and support eliding when height is set.
Browse files Browse the repository at this point in the history
Task-number: QTBUG-22920, QTBUG-22116

Change-Id: Ibe78ce1b0b438eec32955b986a8740f173cd082f
Reviewed-by: Yann Bodson <yann.bodson@nokia.com>
  • Loading branch information
Martin Jones authored and Qt by Nokia committed Nov 28, 2011
1 parent fb237da commit 7d4a187
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 89 deletions.
188 changes: 99 additions & 89 deletions src/declarative/items/qquicktext.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -202,6 +202,7 @@ QSet<QUrl> QQuickTextDocumentWithImageResources::errors;

QQuickTextPrivate::~QQuickTextPrivate()
{
delete elipsisLayout;
delete textLine; textLine = 0;
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -1295,7 +1298,7 @@ void QQuickText::resetHAlign()
{
Q_D(QQuickText);
d->hAlignImplicit = true;
if (d->determineHorizontalAlignment() && isComponentComplete())
if (isComponentComplete() && d->determineHorizontalAlignment())
d->updateLayout();
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -1854,8 +1862,10 @@ void QQuickText::componentComplete()
d->rightToLeftText = d->text.isRightToLeft();
}
d->determineHorizontalAlignment();
d->updateLayout();
}
QQuickItem::componentComplete();
if (d->updateOnComponentComplete)
d->updateLayout();
}


Expand Down
1 change: 1 addition & 0 deletions src/declarative/items/qquicktext_p_p.h
Expand Up @@ -140,6 +140,7 @@ class Q_AUTOTEST_EXPORT QQuickTextPrivate : public QQuickImplicitSizeItemPrivate
bool isLinkActivatedConnected();
QString anchorAt(const QPointF &pos);
QTextLayout layout;
QTextLayout *elipsisLayout;
QList<QRectF> linesRects;
QQuickTextLine *textLine;

Expand Down
10 changes: 10 additions & 0 deletions 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"
}

44 changes: 44 additions & 0 deletions tests/auto/declarative/qquicktext/tst_qquicktext.cpp
Expand Up @@ -70,6 +70,7 @@ private slots:
void width();
void wrap();
void elide();
void multilineElide();
void textFormat();

void alignments_data();
Expand Down Expand Up @@ -448,6 +449,49 @@ void tst_qquicktext::elide()
}
}

void tst_qquicktext::multilineElide()
{
QQuickView *canvas = createView(TESTDATA("multilineelide.qml"));

QQuickText *myText = qobject_cast<QQuickText*>(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()
{
{
Expand Down

0 comments on commit 7d4a187

Please sign in to comment.