Add QFontMetrics(F)::horizontalAdvance() function

A source of constant confusion is the QFontMetrics::width() function,
which does not return the bounding width of the text, but the advance
width. We deprecate this and add horizontalAdvance() instead, to avoid the
confusion in the future.

Note that there was an internal width() overload which was only there
for the purpose of supporting the Qt::TextBypassShaping flag. This
flag has already been replaced by public API, so no such
overload is added. Instead, we deprecate the TextBypassShaping flag
as well, which makes sense, since a replacement has been made.

Also note that there was a consistency problem with QFontMetrics and
QFontMetricsF, which are supposed to be interchangeable. The
QFontMetrics::width() functions for strings took an optional int length
argument, while the floating point version did not. This error is
corrected in the advance() functions.

[ChangeLog][QtGui][Text] Added QFontMetrics::horizontalAdvance() and
QFontMetricsF::horizontalAdvance() to replace the confusingly named
width() function. The latter has now been deprecated.

Change-Id: I0dfda43aa65c8235be32c62fade82cae05b29c79
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Eskil Abrahamsen Blomfeldt 2017-08-02 11:38:10 +02:00
parent 0f0726d36e
commit ee2ad9df70
5 changed files with 167 additions and 30 deletions

View File

@ -243,7 +243,7 @@ public:
// size of a multi-variant string.
TextLongestVariant = 0x80000
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
, TextBypassShaping = 0x100000
#endif
};

View File

@ -5850,7 +5850,7 @@ void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justif
if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
return;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (tf & Qt::TextBypassShaping) {
// Skip complex shaping, shape using glyph advances only
int len = str.length();

View File

@ -510,6 +510,7 @@ int QFontMetrics::rightBearing(QChar ch) const
return qRound(rb);
}
#if QT_DEPRECATED_SINCE(5, 11)
/*!
Returns the width in pixels of the first \a len characters of \a
text. If \a len is negative (the default), the entire string is
@ -520,11 +521,13 @@ int QFontMetrics::rightBearing(QChar ch) const
string will cover whereas width() returns the distance to where
the next string should be drawn.
\deprecated in Qt 5.11. Use horizontalAdvance() instead.
\sa boundingRect()
*/
int QFontMetrics::width(const QString &text, int len) const
{
return width(text, len, 0);
return horizontalAdvance(text, len);
}
/*!
@ -532,17 +535,17 @@ int QFontMetrics::width(const QString &text, int len) const
*/
int QFontMetrics::width(const QString &text, int len, int flags) const
{
int pos = text.indexOf(QLatin1Char('\x9c'));
if (pos != -1) {
len = (len < 0) ? pos : qMin(pos, len);
} else if (len < 0) {
len = text.length();
}
if (len == 0)
return 0;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (flags & Qt::TextBypassShaping) {
int pos = text.indexOf(QLatin1Char('\x9c'));
if (pos != -1) {
len = (len < 0) ? pos : qMin(pos, len);
} else if (len < 0) {
len = text.length();
}
if (len == 0)
return 0;
// Skip complex shaping, only use advances
int numGlyphs = len;
QVarLengthGlyphLayoutArray glyphs(numGlyphs);
@ -555,10 +558,11 @@ int QFontMetrics::width(const QString &text, int len, int flags) const
width += glyphs.advances[i];
return qRound(width);
}
#else
Q_UNUSED(flags)
#endif
QStackTextEngine layout(text, QFont(d.data()));
return qRound(layout.width(0, len));
return horizontalAdvance(text, len);
}
/*!
@ -577,6 +581,8 @@ int QFontMetrics::width(const QString &text, int len, int flags) const
in this particular font are both negative, while the bearings of
"o" are both positive.
\deprecated in Qt 5.11. Use horizontalAdvance() instead.
\warning This function will produce incorrect results for Arabic
characters or non-spacing marks in the middle of a string, as the
glyph shaping and positioning of marks that happens when
@ -586,6 +592,65 @@ int QFontMetrics::width(const QString &text, int len, int flags) const
\sa boundingRect()
*/
int QFontMetrics::width(QChar ch) const
{
return horizontalAdvance(ch);
}
#endif // QT_DEPRECATED_SINCE(5, 11)
/*!
Returns the horizontal advance in pixels of the first \a len characters of \a
text. If \a len is negative (the default), the entire string is
used.
This is the distance appropriate for drawing a subsequent character
after \a text.
\since 5.11
\sa boundingRect()
*/
int QFontMetrics::horizontalAdvance(const QString &text, int len) const
{
int pos = text.indexOf(QLatin1Char('\x9c'));
if (pos != -1) {
len = (len < 0) ? pos : qMin(pos, len);
} else if (len < 0) {
len = text.length();
}
if (len == 0)
return 0;
QStackTextEngine layout(text, QFont(d.data()));
return qRound(layout.width(0, len));
}
/*!
\overload
\image bearings.png Bearings
Returns the horizontal advance of character \a ch in pixels. This is a
distance appropriate for drawing a subsequent character after \a
ch.
Some of the metrics are described in the image. The
central dark rectangles cover the logical horizontalAdvance() of each
character. The outer pale rectangles cover the leftBearing() and
rightBearing() of each character. Notice that the bearings of "f"
in this particular font are both negative, while the bearings of
"o" are both positive.
\warning This function will produce incorrect results for Arabic
characters or non-spacing marks in the middle of a string, as the
glyph shaping and positioning of marks that happens when
processing strings cannot be taken into account. When implementing
an interactive text control, use QTextLayout instead.
\since 5.11
\sa boundingRect()
*/
int QFontMetrics::horizontalAdvance(QChar ch) const
{
if (QChar::category(ch.unicode()) == QChar::Mark_NonSpacing)
return 0;
@ -677,7 +742,7 @@ int QFontMetrics::charWidth(const QString &text, int pos) const
rectangle might be different than what the width() method returns.
If you want to know the advance width of the string (to lay out
a set of strings next to each other), use width() instead.
a set of strings next to each other), use horizontalAdvance() instead.
Newline characters are processed as normal characters, \e not as
linebreaks.
@ -711,7 +776,7 @@ QRect QFontMetrics::boundingRect(const QString &text) const
base line.
\warning The width of the returned rectangle is not the advance width
of the character. Use boundingRect(const QString &) or width() instead.
of the character. Use boundingRect(const QString &) or horizontalAdvance() instead.
\sa width()
*/
@ -846,7 +911,7 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA
rectangle might be different than what the width() method returns.
If you want to know the advance width of the string (to lay out
a set of strings next to each other), use width() instead.
a set of strings next to each other), use horizontalAdvance() instead.
Newline characters are processed as normal characters, \e not as
linebreaks.
@ -1396,6 +1461,7 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
}
#if QT_DEPRECATED_SINCE(5, 11)
/*!
Returns the width in pixels of the characters in the given \a text.
@ -1404,16 +1470,13 @@ qreal QFontMetricsF::rightBearing(QChar ch) const
describing the pixels this string will cover whereas width()
returns the distance to where the next string should be drawn.
\deprecated in Qt 5.11. Use horizontalAdvance() instead.
\sa boundingRect()
*/
qreal QFontMetricsF::width(const QString &text) const
{
int pos = text.indexOf(QLatin1Char('\x9c'));
int len = (pos != -1) ? pos : text.length();
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
return layout.width(0, len).toReal();
return horizontalAdvance(text);
}
/*!
@ -1432,6 +1495,8 @@ qreal QFontMetricsF::width(const QString &text) const
in this particular font are both negative, while the bearings of
"o" are both positive.
\deprecated in Qt 5.11. Use horizontalAdvance() instead.
\warning This function will produce incorrect results for Arabic
characters or non-spacing marks in the middle of a string, as the
glyph shaping and positioning of marks that happens when
@ -1441,6 +1506,66 @@ qreal QFontMetricsF::width(const QString &text) const
\sa boundingRect()
*/
qreal QFontMetricsF::width(QChar ch) const
{
return horizontalAdvance(ch);
}
#endif
/*!
Returns the horizontal advance in pixels of the first \a length characters of \a
text. If \a length is negative (the default), the entire string is
used.
The advance is the distance appropriate for drawing a subsequent
character after \a text.
\since 5.11
\sa boundingRect()
*/
qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const
{
int pos = text.indexOf(QLatin1Char('\x9c'));
if (pos != -1)
length = (length < 0) ? pos : qMin(pos, length);
else if (length < 0)
length = text.length();
if (length == 0)
return 0;
QStackTextEngine layout(text, QFont(d.data()));
layout.itemize();
return layout.width(0, length).toReal();
}
/*!
\overload
\image bearings.png Bearings
Returns the horizontal advance of character \a ch in pixels. This is a
distance appropriate for drawing a subsequent character after \a
ch.
Some of the metrics are described in the image to the right. The
central dark rectangles cover the logical width() of each
character. The outer pale rectangles cover the leftBearing() and
rightBearing() of each character. Notice that the bearings of "f"
in this particular font are both negative, while the bearings of
"o" are both positive.
\warning This function will produce incorrect results for Arabic
characters or non-spacing marks in the middle of a string, as the
glyph shaping and positioning of marks that happens when
processing strings cannot be taken into account. When implementing
an interactive text control, use QTextLayout instead.
\since 5.11
\sa boundingRect()
*/
qreal QFontMetricsF::horizontalAdvance(QChar ch) const
{
if (ch.category() == QChar::Mark_NonSpacing)
return 0.;
@ -1467,6 +1592,7 @@ qreal QFontMetricsF::width(QChar ch) const
return advance.toReal();
}
/*!
Returns the bounding rectangle of the characters in the string
specified by \a text. The bounding rectangle always covers at least
@ -1477,7 +1603,7 @@ qreal QFontMetricsF::width(QChar ch) const
rectangle might be different than what the width() method returns.
If you want to know the advance width of the string (to lay out
a set of strings next to each other), use width() instead.
a set of strings next to each other), use horizontalAdvance() instead.
Newline characters are processed as normal characters, \e not as
linebreaks.
@ -1650,7 +1776,7 @@ QSizeF QFontMetricsF::size(int flags, const QString &text, int tabStops, int *ta
rectangle might be different than what the width() method returns.
If you want to know the advance width of the string (to lay out
a set of strings next to each other), use width() instead.
a set of strings next to each other), use horizontalAdvance() instead.
Newline characters are processed as normal characters, \e not as
linebreaks.

View File

@ -90,10 +90,16 @@ public:
int leftBearing(QChar) const;
int rightBearing(QChar) const;
#if QT_DEPRECATED_SINCE(5, 11)
int width(const QString &, int len = -1) const;
int width(const QString &, int len, int flags) const;
int width(QChar) const;
#endif
int horizontalAdvance(const QString &, int len = -1) const;
int horizontalAdvance(QChar) const;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
QT_DEPRECATED int charWidth(const QString &str, int pos) const;
#endif
@ -164,9 +170,14 @@ public:
qreal leftBearing(QChar) const;
qreal rightBearing(QChar) const;
qreal width(const QString &string) const;
#if QT_DEPRECATED_SINCE(5, 11)
qreal width(const QString &string) const;
qreal width(QChar) const;
#endif
qreal horizontalAdvance(const QString &string, int length = -1) const;
qreal horizontalAdvance(QChar) const;
QRectF boundingRect(const QString &string) const;
QRectF boundingRect(QChar) const;

View File

@ -48,7 +48,7 @@ private slots:
void veryNarrowElidedText();
void averageCharWidth();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void bypassShaping();
#endif
@ -191,7 +191,7 @@ void tst_QFontMetrics::averageCharWidth()
QVERIFY(fmf.averageCharWidth() != 0);
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void tst_QFontMetrics::bypassShaping()
{
QFont f;