From 3cebc02818d2025cda96528bbf289db15ef1bf89 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 26 Mar 2010 13:38:32 +0100 Subject: Change QStaticText::setMaximumSize() to setTextWidth() To avoid having to precalculate the height of the laid out text, we now only supply a maximum text width to QStaticText. The only usage of the maximum height would be to clip the results, and clipping should be set separately from the QStaticText call, since this has no impact on the layout of the glyphs. The tests have been updated to reflect the change in logic. We also need a consistent way of specifying the position of the text. Before, the position meant "baseline position" for unbroken text and "top left position" for text with a specified layout width. We want to be consistent, and since baseline position makes no sense for multiline text, we standardize on top left position. Task-number: QTBUG-9029 Reviewed-by: Gunnar --- src/gui/painting/qpainter.cpp | 42 +++++++-------- src/gui/painting/qpainter.h | 6 +-- src/gui/text/qstatictext.cpp | 86 +++++++++++++++++------------- src/gui/text/qstatictext.h | 6 +-- src/gui/text/qstatictext_p.h | 7 ++- tests/auto/qstatictext/tst_qstatictext.cpp | 51 +++++++++++------- 6 files changed, 109 insertions(+), 89 deletions(-) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index ac5c8b7..7856881 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5762,19 +5762,24 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit /*! - \fn void QPainter::drawStaticText(const QPoint &position, const QStaticText &staticText) + \fn void QPainter::drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText) \since 4.7 \overload - Draws the \a staticText at the \a position. + Draws the \a staticText at the \a topLeftPosition. + + \note The y-position is used as the top of the font. + */ /*! - \fn void QPainter::drawStaticText(int x, int y, const QStaticText &staticText) + \fn void QPainter::drawStaticText(int left, int top, const QStaticText &staticText) \since 4.7 \overload - Draws the \a staticText at coordinates \a x and \a y. + Draws the \a staticText at coordinates \a left and \a top. + + \note The y-position is used as the top of the font. */ /*! @@ -5802,7 +5807,7 @@ void QPainter::drawText(const QPointF &p, const QString &str) /*! \since 4.7 - Draws the given \a staticText at the given \a position. + Draws the given \a staticText at the given \a topLeftPosition. The text will be drawn using the font and the transformation set on the painter. If the font and/or transformation set on the painter are different from the ones used to initialize @@ -5810,15 +5815,17 @@ void QPainter::drawText(const QPointF &p, const QString &str) QStaticText::prepare() to initialize \a staticText with the font and transformation with which it will later be drawn. - If \a position is not the same as when \a staticText was initialized, or when it was last drawn, - then there will be a slight overhead when translating the text to its new position. + If \a topLeftPosition is not the same as when \a staticText was initialized, or when it was + last drawn, then there will be a slight overhead when translating the text to its new position. - \note If the painter's transformation is not affine, then \a staticText will be drawn using regular - calls to drawText(), losing any potential performance improvement. + \note If the painter's transformation is not affine, then \a staticText will be drawn using + regular calls to drawText(), losing any potential for performance improvement. + + \note The y-position is used as the top of the font. \sa QStaticText */ -void QPainter::drawStaticText(const QPointF &position, const QStaticText &staticText) +void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText) { Q_D(QPainter); if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen) @@ -5830,13 +5837,13 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static // If we don't have an extended paint engine, or if the painter is projected, // we go through standard code path if (d->extended == 0 || !d->state->matrix.isAffine()) { - staticText_d->paintText(position, this); + staticText_d->paintText(topLeftPosition, this); return; } // Don't recalculate entire layout because of translation, rather add the dx and dy // into the position to move each text item the correct distance. - QPointF transformedPosition = position * d->state->matrix; + QPointF transformedPosition = topLeftPosition * d->state->matrix; QTransform matrix = d->state->matrix; // The translation has been applied to transformedPosition. Remove translation @@ -5863,14 +5870,6 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static staticTextNeedsReinit = true; } - bool restoreWhenFinished = false; - if (staticText_d->needsClipRect) { - save(); - setClipRect(QRectF(position, staticText_d->maximumSize)); - - restoreWhenFinished = true; - } - if (font() != staticText_d->font) { staticText_d->font = font(); staticTextNeedsReinit = true; @@ -5910,9 +5909,6 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static if (currentColor != oldPen.color()) setPen(oldPen); - if (restoreWhenFinished) - restore(); - if (matrix.isTranslating()) d->state->matrix = matrix; } diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 443925b..edfb67e 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -396,9 +396,9 @@ public: void setLayoutDirection(Qt::LayoutDirection direction); Qt::LayoutDirection layoutDirection() const; - void drawStaticText(const QPointF &p, const QStaticText &staticText); - inline void drawStaticText(const QPoint &p, const QStaticText &staticText); - inline void drawStaticText(int x, int y, const QStaticText &staticText); + void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText); + inline void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText); + inline void drawStaticText(int left, int top, const QStaticText &staticText); void drawText(const QPointF &p, const QString &s); inline void drawText(const QPoint &p, const QString &s); diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index f433c78..977355e 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -143,11 +143,10 @@ QStaticText::QStaticText() If an invalid size is passed for \a size the text will be unbounded. */ -QStaticText::QStaticText(const QString &text, const QSizeF &size) +QStaticText::QStaticText(const QString &text) : data(new QStaticTextPrivate) { data->text = text; - data->maximumSize = size; data->init(); } @@ -209,7 +208,7 @@ QStaticText &QStaticText::operator=(const QStaticText &other) } /*! - Compares \a other to this QStaticText. Returns true if the texts, fonts and maximum sizes + Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths are equal. */ bool QStaticText::operator==(const QStaticText &other) const @@ -217,7 +216,7 @@ bool QStaticText::operator==(const QStaticText &other) const return (data == other.data || (data->text == other.data->text && data->font == other.data->font - && data->maximumSize == other.data->maximumSize)); + && data->textWidth == other.data->textWidth)); } /*! @@ -315,33 +314,39 @@ QStaticText::PerformanceHint QStaticText::performanceHint() const } /*! - Sets the maximum size of the QStaticText to \a size. + Sets the preferred width for this QStaticText. If the text is wider than the specified width, + it will be broken into multiple lines and grow vertically. If the text cannot be split into + multiple lines, it will be larger than the specified \a textWidth. + + Setting the preferred text width to a negative number will cause the text to be unbounded. + + Use size() to get the actual size of the text. \note This function will cause the layout of the text to be recalculated. - \sa maximumSize(), size() + \sa textWidth(), size() */ -void QStaticText::setMaximumSize(const QSizeF &size) +void QStaticText::setTextWidth(qreal textWidth) { detach(); - data->maximumSize = size; + data->textWidth = textWidth; data->init(); } /*! - Returns the maximum size of the QStaticText. + Returns the preferred width for this QStaticText. - \sa setMaximumSize() + \sa setTextWidth() */ -QSizeF QStaticText::maximumSize() const +qreal QStaticText::textWidth() const { - return data->maximumSize; + return data->textWidth; } /*! Returns the size of the bounding rect for this QStaticText. - \sa maximumSize() + \sa textWidth() */ QSizeF QStaticText::size() const { @@ -349,14 +354,14 @@ QSizeF QStaticText::size() const } QStaticTextPrivate::QStaticTextPrivate() - : items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), + : items(0), itemCount(0), glyphPool(0), positionPool(0), textWidth(-1.0), useBackendOptimizations(false), textFormat(Qt::AutoText) { } QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) - : text(other.text), font(other.font), maximumSize(other.maximumSize), matrix(other.matrix), - items(0), itemCount(0), glyphPool(0), positionPool(0), needsClipRect(false), + : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix), + items(0), itemCount(0), glyphPool(0), positionPool(0), useBackendOptimizations(other.useBackendOptimizations), textFormat(other.textFormat) { } @@ -539,43 +544,50 @@ namespace { }; } -void QStaticTextPrivate::paintText(const QPointF &pos, QPainter *p) +void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p) { bool preferRichText = textFormat == Qt::RichText || (textFormat == Qt::AutoText && Qt::mightBeRichText(text)); if (!preferRichText) { - if (maximumSize.isValid()) { - QRectF boundingRect; - p->drawText(QRectF(pos, maximumSize), Qt::TextWordWrap, text, &boundingRect); - - actualSize = boundingRect.size(); - needsClipRect = boundingRect.width() > maximumSize.width() - || boundingRect.height() > maximumSize.height(); - } else { - p->drawText(pos, text); - needsClipRect = false; - - QFontMetrics fm(font); - actualSize = fm.boundingRect(text).size(); + QTextLayout textLayout; + textLayout.setText(text); + textLayout.setFont(font); + + qreal leading = QFontMetricsF(font).leading(); + qreal height = -leading; + + textLayout.beginLayout(); + while (1) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + + if (textWidth >= 0.0) + line.setLineWidth(textWidth); + height += leading; + line.setPosition(QPointF(0.0, height)); + height += line.height(); } + textLayout.endLayout(); + + actualSize = textLayout.boundingRect().size(); + textLayout.draw(p, topLeftPosition); } else { QTextDocument document; document.setDefaultFont(font); document.setDocumentMargin(0.0); + if (textWidth >= 0.0) + document.setTextWidth(textWidth); document.setHtml(text); - QPointF adjustedPos = pos - QPointF(0, QFontMetricsF(font).ascent()); - QRectF rect = maximumSize.isValid() ? QRectF(adjustedPos, maximumSize) : QRectF(); document.adjustSize(); p->save(); - p->translate(adjustedPos); - document.drawContents(p, rect); + p->translate(topLeftPosition); + document.drawContents(p); p->restore(); + actualSize = document.size(); - needsClipRect = maximumSize.isValid() - && (actualSize.width() > maximumSize.width() - || actualSize.height() > maximumSize.height()); } } diff --git a/src/gui/text/qstatictext.h b/src/gui/text/qstatictext.h index 00d42e0..c37194c 100644 --- a/src/gui/text/qstatictext.h +++ b/src/gui/text/qstatictext.h @@ -66,7 +66,7 @@ public: }; QStaticText(); - QStaticText(const QString &text, const QSizeF &maximumSize = QSizeF()); + QStaticText(const QString &text); QStaticText(const QStaticText &other); ~QStaticText(); @@ -76,8 +76,8 @@ public: void setTextFormat(Qt::TextFormat textFormat); Qt::TextFormat textFormat() const; - void setMaximumSize(const QSizeF &maximumSize); - QSizeF maximumSize() const; + void setTextWidth(qreal textWidth); + qreal textWidth() const; QSizeF size() const; diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h index e758244..3bbc61f 100644 --- a/src/gui/text/qstatictext_p.h +++ b/src/gui/text/qstatictext_p.h @@ -122,7 +122,7 @@ public: QString text; // 4 bytes per text QFont font; // 8 bytes per text - QSizeF maximumSize; // 16 bytes per text + qreal textWidth; // 8 bytes per text QSizeF actualSize; // 16 bytes per text QPointF position; // 16 bytes per text @@ -132,11 +132,10 @@ public: glyph_t *glyphPool; // 4 bytes per text QFixedPoint *positionPool; // 4 bytes per text - unsigned char needsClipRect : 1; // 1 byte per text - unsigned char useBackendOptimizations : 1; + unsigned char useBackendOptimizations : 1; // 1 byte per text unsigned char textFormat : 2; // ================ - // 171 bytes per text + // 163 bytes per text static QStaticTextPrivate *get(const QStaticText *q); }; diff --git a/tests/auto/qstatictext/tst_qstatictext.cpp b/tests/auto/qstatictext/tst_qstatictext.cpp index b59c10b..4e2d906 100644 --- a/tests/auto/qstatictext/tst_qstatictext.cpp +++ b/tests/auto/qstatictext/tst_qstatictext.cpp @@ -50,7 +50,7 @@ #include #include -// #define DEBUG_SAVE_IMAGE +#define DEBUG_SAVE_IMAGE class tst_QStaticText: public QObject { @@ -69,7 +69,7 @@ private slots: void drawToRect_data(); void drawToRect(); void setFont(); - void setMaximumSize(); + void setTextWidth(); void prepareToCorrectData(); void prepareToWrongData(); @@ -127,7 +127,7 @@ void tst_QStaticText::drawToPoint() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); text.setPerformanceHint(performanceHint); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -156,12 +156,19 @@ void tst_QStaticText::drawToRect() imageDrawStaticText.fill(Qt::white); { QPainter p(&imageDrawStaticText); - QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", QSizeF(10, 500)); + QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + text.setTextWidth(10), + p.setClipRect(QRectF(11, 12, 10, 500)); text.setPerformanceHint(performanceHint); text.setTextFormat(Qt::PlainText); p.drawStaticText(QPointF(11, 12), text); } +#if defined(DEBUG_SAVE_IMAGE) + imageDrawText.save("drawToRect_imageDrawText.png"); + imageDrawStaticText.save("drawToRect_imageDrawStaticText.png"); +#endif + QCOMPARE(imageDrawStaticText, imageDrawText); } @@ -187,7 +194,7 @@ void tst_QStaticText::prepareToCorrectData() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.prepare(transform, p.font()); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } if (!supportsTransformations()) @@ -215,7 +222,7 @@ void tst_QStaticText::prepareToWrongData() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.prepare(transform, p.font()); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -232,10 +239,10 @@ void tst_QStaticText::setFont() imageDrawText.fill(Qt::white); { QPainter p(&imageDrawText); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(0, 0 + QFontMetrics(p.font()).ascent(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); p.setFont(font); - p.drawText(11, 120, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(11, 120 + QFontMetrics(p.font()).ascent(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -253,10 +260,15 @@ void tst_QStaticText::setFont() p.drawStaticText(11, 120, text); } +#if defined(DEBUG_SAVE_IMAGE) + imageDrawText.save("setFont_imageDrawText.png"); + imageDrawStaticText.save("setFont_imageDrawStaticText.png"); +#endif + QCOMPARE(imageDrawStaticText, imageDrawText); } -void tst_QStaticText::setMaximumSize() +void tst_QStaticText::setTextWidth() { QPixmap imageDrawText(1000, 1000); imageDrawText.fill(Qt::white); @@ -270,7 +282,8 @@ void tst_QStaticText::setMaximumSize() { QPainter p(&imageDrawStaticText); QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); - text.setMaximumSize(QSizeF(10, 500)); + text.setTextWidth(10); + p.setClipRect(QRectF(11, 12, 10, 500)); p.drawStaticText(QPointF(11, 12), text); } @@ -297,7 +310,7 @@ void tst_QStaticText::translatedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -329,7 +342,7 @@ void tst_QStaticText::rotatedPainter() { QPainter p(&imageDrawText); p.rotate(30.0); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(0, 0 + QFontMetricsF(p.font()).ascent(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -373,7 +386,7 @@ void tst_QStaticText::scaledPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } if (!supportsTransformations()) @@ -404,7 +417,7 @@ void tst_QStaticText::projectedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } QCOMPARE(imageDrawStaticText, imageDrawText); @@ -434,7 +447,7 @@ void tst_QStaticText::rotatedScaledAndTranslatedPainter() QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); text.setTextFormat(Qt::PlainText); - p.drawStaticText(QPointF(11, 12), text); + p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text); } #if defined(DEBUG_SAVE_IMAGE) @@ -456,10 +469,10 @@ void tst_QStaticText::transformationChanged() p.rotate(33.0); p.scale(0.5, 0.7); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(0, 0 + QFontMetricsF(p.font()).ascent(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); p.scale(7.0, 5.0); - p.drawText(0, 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); + p.drawText(0, 0 + QFontMetricsF(p.font()).ascent(), "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); } QPixmap imageDrawStaticText(1000, 1000); @@ -566,7 +579,7 @@ void tst_QStaticText::setPenRichText() QStaticText staticText; staticText.setText("XXXXX"); staticText.setTextFormat(Qt::RichText); - p.drawStaticText(0, fm.ascent(), staticText); + p.drawStaticText(0, 0, staticText); } QImage img = image.toImage(); @@ -595,7 +608,7 @@ void tst_QStaticText::richTextOverridesPen() QStaticText staticText; staticText.setText("XXXXX"); staticText.setTextFormat(Qt::RichText); - p.drawStaticText(0, fm.ascent(), staticText); + p.drawStaticText(0, 0, staticText); } QImage img = image.toImage(); -- cgit v0.12