Skip to content

Commit

Permalink
Fix text wrapping and eliding with implicitWidth.
Browse files Browse the repository at this point in the history
Update the implicitWidth of the item before continuing with the layout
rather than after it's completed this gives any bindings dependent on
the implicitWidth the opportunity to update the width before the layout
continues.

Task-number: QTBUG-22680
Task-number: QTBUG-24251
Change-Id: I61cd96ad9891b22d8b83937ad2c06719f88976b6
Reviewed-by: Martin Jones <martin.jones@nokia.com>
  • Loading branch information
Andrew den Exter authored and Qt by Nokia committed Mar 22, 2012
1 parent 793a01d commit 336c338
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 35 deletions.
51 changes: 41 additions & 10 deletions src/quick/items/qquicktext.cpp
Expand Up @@ -421,6 +421,10 @@ void QQuickTextPrivate::updateSize()
//setup instance of QTextLayout for all cases other than richtext
if (!richText) {
QRectF textRect = setupTextLayout(&naturalWidth);

if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
return; // get this far we'll get a warning to that effect if it is.

layedOutTextRect = textRect;
size = textRect.size();
dy -= size.height();
Expand All @@ -443,7 +447,13 @@ void QQuickTextPrivate::updateSize()
if (requireImplicitWidth && q->widthValid()) {
extra->doc->setTextWidth(-1);
naturalWidth = extra->doc->idealWidth();
const bool wasInLayout = internalWidthUpdate;
internalWidthUpdate = true;
q->setImplicitWidth(naturalWidth);
internalWidthUpdate = wasInLayout;
}
if (internalWidthUpdate)
return;
if (wrapMode != QQuickText::NoWrap && q->widthValid())
extra->doc->setTextWidth(q->width());
else
Expand All @@ -468,8 +478,6 @@ void QQuickTextPrivate::updateSize()
qreal iWidth = -1;
if (!q->widthValid())
iWidth = size.width();
else if (requireImplicitWidth)
iWidth = naturalWidth;
if (iWidth > -1)
q->setImplicitSize(iWidth, size.height());
internalWidthUpdate = false;
Expand Down Expand Up @@ -697,14 +705,19 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
layout.endLayout();
*naturalWidth = layout.maximumWidth();
layout.clearLayout();

bool wasInLayout = internalWidthUpdate;
internalWidthUpdate = true;
q->setImplicitWidth(*naturalWidth);
internalWidthUpdate = wasInLayout;
}

QFontMetrics fm(font);
qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : fm.height() * lineHeight();
return QRect(0, 0, 0, height);
}

const qreal lineWidth = q->widthValid() ? q->width() : FLT_MAX;
qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;

const bool customLayout = isLineLaidOutConnected();
Expand Down Expand Up @@ -735,6 +748,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
QTextLine line;
int visibleCount = 0;
bool elide;
bool widthChanged;
qreal height = 0;
QString elideText;
bool once = true;
Expand All @@ -755,13 +769,14 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
scaledFont.setPointSize(scaledFontSize);
layout.setFont(scaledFont);
}
layout.beginLayout();

layout.beginLayout();

bool wrapped = false;
bool truncateHeight = false;
truncated = false;
elide = false;
widthChanged = false;
int characterCount = 0;
int unwrappedLineCount = 1;
int maxLineCount = maximumLineCount();
Expand Down Expand Up @@ -864,7 +879,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
br = br.united(line.naturalTextRect());
line = nextLine;
}

layout.endLayout();
br.moveTop(0);

Expand All @@ -886,8 +900,21 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
if (!line.isValid())
break;
}

*naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth());
}

bool wasInLayout = internalWidthUpdate;
internalWidthUpdate = true;
q->setImplicitWidth(*naturalWidth);
internalWidthUpdate = wasInLayout;

const qreal oldWidth = lineWidth;
lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) {
widthChanged = true;
continue;
}
}

// If the next needs to be elided and there's an abbreviated string available
Expand All @@ -911,32 +938,36 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
if (horizontalFit) {
if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
largeFont = scaledFontSize - 1;
scaledFontSize = (smallFont + largeFont) / 2;
if (smallFont > largeFont)
break;
scaledFontSize = (smallFont + largeFont) / 2;
if (pixelSize)
scaledFont.setPixelSize(scaledFontSize);
else
scaledFont.setPointSize(scaledFontSize);
continue;
} else if (!verticalFit) {
smallFont = scaledFontSize;
scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont == largeFont)
break;
scaledFontSize = (smallFont + largeFont + 1) / 2;
}
}

if (verticalFit) {
if (truncateHeight || unelidedRect.height() > maxHeight) {
largeFont = scaledFontSize - 1;
scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont > largeFont)
break;
scaledFontSize = (smallFont + largeFont) / 2;

} else {
smallFont = scaledFontSize;
scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont == largeFont)
break;
scaledFontSize = (smallFont + largeFont + 1) / 2;
}
}

}

if (eos != multilengthEos)
Expand Down
16 changes: 12 additions & 4 deletions src/quick/items/qquicktextedit.cpp
Expand Up @@ -1143,7 +1143,8 @@ void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
void QQuickTextEdit::geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry)
{
if (newGeometry.width() != oldGeometry.width())
Q_D(QQuickTextEdit);
if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap && !d->inLayout)
updateSize();
QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
Expand Down Expand Up @@ -1857,6 +1858,13 @@ void QQuickTextEdit::updateSize()
if (d->requireImplicitWidth) {
d->document->setTextWidth(-1);
naturalWidth = d->document->idealWidth();

const bool wasInLayout = d->inLayout;
d->inLayout = true;
setImplicitWidth(naturalWidth);
d->inLayout = wasInLayout;
if (d->inLayout) // probably the result of a binding loop, but by letting it
return; // get this far we'll get a warning to that effect.
}
if (d->document->textWidth() != width())
d->document->setTextWidth(width());
Expand Down Expand Up @@ -1888,11 +1896,11 @@ void QQuickTextEdit::updateSize()
d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
// ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
qreal iWidth = -1;
if (!widthValid())
if (!widthValid() && !d->requireImplicitWidth)
iWidth = newWidth;
else if (d->requireImplicitWidth)
iWidth = naturalWidth;

qreal newHeight = d->document->isEmpty() ? fm.height() : d->document->size().height();

if (iWidth > -1)
setImplicitSize(iWidth, newHeight);
else
Expand Down
3 changes: 2 additions & 1 deletion src/quick/items/qquicktextedit_p_p.h
Expand Up @@ -78,7 +78,7 @@ class QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
, documentDirty(true), dirty(false), richText(false), cursorVisible(false)
, focusOnPress(true), persistentSelection(false), requireImplicitWidth(false)
, selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true)
, rightToLeftText(false), textCached(false)
, rightToLeftText(false), textCached(false), inLayout(false)
{
}

Expand Down Expand Up @@ -144,6 +144,7 @@ class QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate
bool hAlignImplicit:1;
bool rightToLeftText:1;
bool textCached:1;
bool inLayout:1;
};

QT_END_NAMESPACE
Expand Down
51 changes: 49 additions & 2 deletions src/quick/items/qquicktextinput.cpp
Expand Up @@ -1604,9 +1604,11 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry)
{
Q_D(QQuickTextInput);
if (newGeometry.width() != oldGeometry.width())
if (!d->inLayout) {
if (newGeometry.width() != oldGeometry.width() && d->wrapMode != NoWrap)
d->updateLayout();
updateCursorRectangle();
}
QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}

Expand Down Expand Up @@ -2699,6 +2701,38 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
}
}

qreal QQuickTextInputPrivate::getImplicitWidth() const
{
Q_Q(const QQuickTextInput);
if (!requireImplicitWidth) {
QQuickTextInputPrivate *d = const_cast<QQuickTextInputPrivate *>(this);
d->requireImplicitWidth = true;

if (q->isComponentComplete()) {
// One time cost, only incurred if implicitWidth is first requested after
// componentComplete.
QTextLayout layout(m_text);

QTextOption option = m_textLayout.textOption();
option.setTextDirection(m_layoutDirection);
option.setFlags(QTextOption::IncludeTrailingSpaces);
option.setWrapMode(QTextOption::WrapMode(wrapMode));
option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
layout.setTextOption(option);
layout.setFont(font);
layout.setPreeditArea(m_textLayout.preeditAreaPosition(), m_textLayout.preeditAreaText());
layout.beginLayout();

QTextLine line = layout.createLine();
line.setLineWidth(INT_MAX);
d->implicitWidth = qCeil(line.naturalTextWidth());

layout.endLayout();
}
}
return implicitWidth;
}

void QQuickTextInputPrivate::updateLayout()
{
Q_Q(QQuickTextInput);
Expand All @@ -2718,6 +2752,15 @@ void QQuickTextInputPrivate::updateLayout()
boundingRect = QRectF();
m_textLayout.beginLayout();
QTextLine line = m_textLayout.createLine();
if (requireImplicitWidth) {
line.setLineWidth(INT_MAX);
const bool wasInLayout = inLayout;
inLayout = true;
q->setImplicitWidth(qCeil(line.naturalTextWidth()));
inLayout = wasInLayout;
if (inLayout) // probably the result of a binding loop, but by letting it
return; // get this far we'll get a warning to that effect.
}
qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
qreal height = 0;
do {
Expand All @@ -2737,7 +2780,11 @@ void QQuickTextInputPrivate::updateLayout()

updateType = UpdatePaintNode;
q->update();
q->setImplicitSize(boundingRect.width(), boundingRect.height());

if (!requireImplicitWidth && !q->widthValid())
q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
else
q->setImplicitHeight(qCeil(boundingRect.height()));

if (previousRect != boundingRect)
emit q->contentSizeChanged();
Expand Down
7 changes: 6 additions & 1 deletion src/quick/items/qquicktextinput_p_p.h
Expand Up @@ -125,6 +125,8 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr
, m_acceptableInput(1)
, m_blinkStatus(0)
, m_passwordEchoEditing(false)
, inLayout(false)
, requireImplicitWidth(false)
{
}

Expand Down Expand Up @@ -256,7 +258,8 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr
bool m_acceptableInput : 1;
bool m_blinkStatus : 1;
bool m_passwordEchoEditing : 1;

bool inLayout:1;
bool requireImplicitWidth:1;

static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
return t->d_func();
Expand Down Expand Up @@ -404,6 +407,8 @@ class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPr

void updateLayout();

qreal getImplicitWidth() const;

private:
void removeSelectedText();
void internalSetText(const QString &txt, int pos = -1, bool edited = true);
Expand Down

0 comments on commit 336c338

Please sign in to comment.