| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtDeclarative module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "private/qdeclarativetext_p.h" |
| #include "private/qdeclarativetext_p_p.h" |
| #include <private/qtextdocumentlayout_p.h> |
| #include <qdeclarativestyledtext_p.h> |
| #include <qdeclarativeinfo.h> |
| #include <qdeclarativepixmapcache_p.h> |
| |
| #include <QSet> |
| #include <QTextLayout> |
| #include <QTextLine> |
| #include <QTextDocument> |
| #include <QGraphicsSceneMouseEvent> |
| #include <QPainter> |
| #include <QAbstractTextDocumentLayout> |
| #include <qmath.h> |
| #include <limits.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; |
| |
| class QTextDocumentWithImageResources : public QTextDocument { |
| Q_OBJECT |
| |
| public: |
| QTextDocumentWithImageResources(QDeclarativeText *parent); |
| virtual ~QTextDocumentWithImageResources(); |
| |
| void setText(const QString &); |
| int resourcesLoading() const { return outstanding; } |
| |
| protected: |
| QVariant loadResource(int type, const QUrl &name); |
| |
| private slots: |
| void requestFinished(); |
| |
| private: |
| QHash<QUrl, QDeclarativePixmap *> m_resources; |
| |
| int outstanding; |
| static QSet<QUrl> errors; |
| }; |
| |
| class QDeclarativeTextDocumentLayout : public QTextDocumentLayout |
| { |
| Q_OBJECT |
| public: |
| QDeclarativeTextDocumentLayout(QTextDocument *doc); |
| void setLineHeight(qreal lineHeight, QDeclarativeText::LineHeightMode mode); |
| }; |
| |
| DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); |
| |
| QString QDeclarativeTextPrivate::elideChar = QString(0x2026); |
| |
| QDeclarativeTextPrivate::QDeclarativeTextPrivate() |
| : color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft), |
| vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone), |
| format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), lineHeight(1), |
| lineHeightMode(QDeclarativeText::ProportionalHeight), lineCount(1), truncated(false), maximumLineCount(INT_MAX), |
| maximumLineCountValid(false), imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false), |
| cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), hAlignImplicit(true), |
| rightToLeftText(false), layoutTextElided(false), naturalWidth(0), doc(0) |
| { |
| cacheAllTextAsImage = enableImageCache(); |
| QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; |
| QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; |
| } |
| |
| QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent) |
| : QTextDocument(parent), outstanding(0) |
| { |
| } |
| |
| QTextDocumentWithImageResources::~QTextDocumentWithImageResources() |
| { |
| if (!m_resources.isEmpty()) |
| qDeleteAll(m_resources); |
| } |
| |
| QVariant QTextDocumentWithImageResources::loadResource(int type, const QUrl &name) |
| { |
| QDeclarativeContext *context = qmlContext(parent()); |
| QUrl url = context->resolvedUrl(name); |
| |
| if (type == QTextDocument::ImageResource) { |
| QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url); |
| |
| if (iter == m_resources.end()) { |
| QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url); |
| iter = m_resources.insert(name, p); |
| |
| if (p->isLoading()) { |
| p->connectFinished(this, SLOT(requestFinished())); |
| outstanding++; |
| } |
| } |
| |
| QDeclarativePixmap *p = *iter; |
| if (p->isReady()) { |
| return p->pixmap(); |
| } else if (p->isError()) { |
| if (!errors.contains(url)) { |
| errors.insert(url); |
| qmlInfo(parent()) << p->error(); |
| } |
| } |
| } |
| |
| return QTextDocument::loadResource(type,url); // The *resolved* URL |
| } |
| |
| void QTextDocumentWithImageResources::requestFinished() |
| { |
| outstanding--; |
| if (outstanding == 0) { |
| QDeclarativeText *textItem = static_cast<QDeclarativeText*>(parent()); |
| QString text = textItem->text(); |
| #ifndef QT_NO_TEXTHTMLPARSER |
| setHtml(text); |
| #else |
| setPlainText(text); |
| #endif |
| QDeclarativeTextPrivate *d = QDeclarativeTextPrivate::get(textItem); |
| d->updateLayout(); |
| } |
| } |
| |
| void QTextDocumentWithImageResources::setText(const QString &text) |
| { |
| if (!m_resources.isEmpty()) { |
| qDeleteAll(m_resources); |
| m_resources.clear(); |
| outstanding = 0; |
| } |
| |
| #ifndef QT_NO_TEXTHTMLPARSER |
| setHtml(text); |
| #else |
| setPlainText(text); |
| #endif |
| } |
| |
| QSet<QUrl> QTextDocumentWithImageResources::errors; |
| |
| QDeclarativeTextDocumentLayout::QDeclarativeTextDocumentLayout(QTextDocument *doc) |
| : QTextDocumentLayout(doc) { |
| } |
| |
| void QDeclarativeTextDocumentLayout::setLineHeight(qreal lineHeight, QDeclarativeText::LineHeightMode mode = QDeclarativeText::ProportionalHeight) |
| { |
| QTextDocumentLayout::setLineHeight(lineHeight, QTextDocumentLayout::LineHeightMode(mode)); |
| } |
| |
| QDeclarativeTextPrivate::~QDeclarativeTextPrivate() |
| { |
| } |
| |
| qreal QDeclarativeTextPrivate::implicitWidth() const |
| { |
| if (!requireImplicitWidth) { |
| // We don't calculate implicitWidth unless it is required. |
| // We need to force a size update now to ensure implicitWidth is calculated |
| QDeclarativeTextPrivate *me = const_cast<QDeclarativeTextPrivate*>(this); |
| me->requireImplicitWidth = true; |
| me->updateSize(); |
| } |
| return mImplicitWidth; |
| } |
| |
| void QDeclarativeTextPrivate::updateLayout() |
| { |
| Q_Q(QDeclarativeText); |
| if (!q->isComponentComplete()) { |
| updateOnComponentComplete = true; |
| return; |
| } |
| |
| layoutTextElided = false; |
| // Setup instance of QTextLayout for all cases other than richtext |
| if (!richText) { |
| layout.clearLayout(); |
| layout.setFont(font); |
| if (format != QDeclarativeText::StyledText) { |
| QString tmp = text; |
| tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); |
| singleline = !tmp.contains(QChar::LineSeparator); |
| if (singleline && !maximumLineCountValid && elideMode != QDeclarativeText::ElideNone && q->widthValid()) { |
| QFontMetrics fm(font); |
| tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width()); |
| if (tmp != text) { |
| layoutTextElided = true; |
| if (!truncated) { |
| truncated = true; |
| emit q->truncatedChanged(); |
| } |
| } |
| } |
| layout.setText(tmp); |
| } else { |
| singleline = false; |
| QDeclarativeStyledText::parse(text, layout); |
| } |
| } else { |
| ensureDoc(); |
| QDeclarativeTextDocumentLayout *layout = new QDeclarativeTextDocumentLayout(doc); |
| layout->setLineHeight(lineHeight, lineHeightMode); |
| doc->setDocumentLayout(layout); |
| } |
| |
| updateSize(); |
| } |
| |
| void QDeclarativeTextPrivate::updateSize() |
| { |
| Q_Q(QDeclarativeText); |
| |
| if (!q->isComponentComplete()) { |
| updateOnComponentComplete = true; |
| return; |
| } |
| |
| if (!requireImplicitWidth) { |
| emit q->implicitWidthChanged(); |
| // if the implicitWidth is used, then updateSize() has already been called (recursively) |
| if (requireImplicitWidth) |
| return; |
| } |
| |
| invalidateImageCache(); |
| |
| QFontMetrics fm(font); |
| if (text.isEmpty()) { |
| q->setImplicitWidth(0); |
| q->setImplicitHeight(fm.height()); |
| paintedSize = QSize(0, fm.height()); |
| emit q->paintedSizeChanged(); |
| q->update(); |
| return; |
| } |
| |
| int dy = q->height(); |
| QSize size(0, 0); |
| |
| //setup instance of QTextLayout for all cases other than richtext |
| if (!richText) { |
| QRect textRect = setupTextLayout(); |
| if (layedOutTextRect.size() != textRect.size()) |
| q->prepareGeometryChange(); |
| layedOutTextRect = textRect; |
| size = textRect.size(); |
| dy -= size.height(); |
| } else { |
| singleline = false; // richtext can't elide or be optimized for single-line case |
| ensureDoc(); |
| doc->setDefaultFont(font); |
| |
| QDeclarativeText::HAlignment horizontalAlignment = q->effectiveHAlign(); |
| if (rightToLeftText) { |
| if (horizontalAlignment == QDeclarativeText::AlignLeft) |
| horizontalAlignment = QDeclarativeText::AlignRight; |
| else if (horizontalAlignment == QDeclarativeText::AlignRight) |
| horizontalAlignment = QDeclarativeText::AlignLeft; |
| } |
| QTextOption option; |
| option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign)); |
| option.setWrapMode(QTextOption::WrapMode(wrapMode)); |
| doc->setDefaultTextOption(option); |
| if (requireImplicitWidth && q->widthValid()) { |
| doc->setTextWidth(-1); |
| naturalWidth = doc->idealWidth(); |
| } |
| if (wrapMode != QDeclarativeText::NoWrap && q->widthValid()) |
| doc->setTextWidth(q->width()); |
| else |
| doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) |
| dy -= (int)doc->size().height(); |
| QSize dsize = doc->size().toSize(); |
| if (dsize != layedOutTextRect.size()) { |
| q->prepareGeometryChange(); |
| layedOutTextRect = QRect(QPoint(0,0), dsize); |
| } |
| size = QSize(int(doc->idealWidth()),dsize.height()); |
| } |
| int yoff = 0; |
| |
| if (q->heightValid()) { |
| if (vAlign == QDeclarativeText::AlignBottom) |
| yoff = dy; |
| else if (vAlign == QDeclarativeText::AlignVCenter) |
| yoff = dy/2; |
| } |
| q->setBaselineOffset(fm.ascent() + yoff); |
| |
| //### need to comfirm cost of always setting these for richText |
| internalWidthUpdate = true; |
| if (!q->widthValid()) |
| q->setImplicitWidth(size.width()); |
| else if (requireImplicitWidth) |
| q->setImplicitWidth(naturalWidth); |
| internalWidthUpdate = false; |
| q->setImplicitHeight(size.height()); |
| if (paintedSize != size) { |
| paintedSize = size; |
| emit q->paintedSizeChanged(); |
| } |
| q->update(); |
| } |
| |
| /*! |
| Lays out the QDeclarativeTextPrivate::layout QTextLayout in the constraints of the QDeclarativeText. |
| |
| Returns the size of the final text. This can be used to position the text vertically (the text is |
| already absolutely positioned horizontally). |
| */ |
| QRect QDeclarativeTextPrivate::setupTextLayout() |
| { |
| // ### text layout handling should be profiled and optimized as needed |
| // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); |
| Q_Q(QDeclarativeText); |
| layout.setCacheEnabled(true); |
| |
| qreal lineWidth = 0; |
| int visibleCount = 0; |
| |
| //set manual width |
| if (q->widthValid()) |
| lineWidth = q->width(); |
| |
| QTextOption textOption = layout.textOption(); |
| textOption.setAlignment(Qt::Alignment(q->effectiveHAlign())); |
| textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); |
| layout.setTextOption(textOption); |
| |
| bool elideText = false; |
| bool truncate = false; |
| |
| QFontMetrics fm(layout.font()); |
| elidePos = QPointF(); |
| |
| if (requireImplicitWidth && q->widthValid()) { |
| // requires an extra layout |
| QString elidedText; |
| if (layoutTextElided) { |
| // We have provided elided text to the layout, but we must calculate unelided width. |
| elidedText = layout.text(); |
| layout.setText(text); |
| } |
| layout.beginLayout(); |
| forever { |
| QTextLine line = layout.createLine(); |
| if (!line.isValid()) |
| break; |
| } |
| layout.endLayout(); |
| QRectF br; |
| for (int i = 0; i < layout.lineCount(); ++i) { |
| QTextLine line = layout.lineAt(i); |
| br = br.united(line.naturalTextRect()); |
| } |
| naturalWidth = br.width(); |
| if (layoutTextElided) |
| layout.setText(elidedText); |
| } |
| |
| 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++; |
| if (lineWidth) |
| line.setLineWidth(lineWidth); |
| visibleTextLength += line.textLength(); |
| |
| if (--linesLeft == 0) { |
| if (visibleTextLength < text.length()) { |
| truncate = true; |
| if (elideMode==QDeclarativeText::ElideRight && q->widthValid()) { |
| qreal elideWidth = fm.width(elideChar); |
| // Need to correct for alignment |
| 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; |
| } |
| } |
| } |
| } |
| layout.endLayout(); |
| |
| //Update truncated |
| if (truncated != truncate) { |
| truncated = truncate; |
| emit q->truncatedChanged(); |
| } |
| } else { |
| layout.beginLayout(); |
| forever { |
| QTextLine line = layout.createLine(); |
| if (!line.isValid()) |
| break; |
| visibleCount++; |
| if (lineWidth) |
| line.setLineWidth(lineWidth); |
| } |
| layout.endLayout(); |
| } |
| |
| qreal height = 0; |
| QRectF br; |
| for (int i = 0; i < layout.lineCount(); ++i) { |
| QTextLine line = layout.lineAt(i); |
| // set line spacing |
| 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 == QDeclarativeText::FixedHeight) ? lineHeight : line.height() * lineHeight; |
| } |
| br.setHeight(height); |
| |
| if (!q->widthValid()) |
| naturalWidth = br.width(); |
| |
| //Update the number of visible lines |
| if (lineCount != visibleCount) { |
| lineCount = visibleCount; |
| emit q->lineCountChanged(); |
| } |
| |
| return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height())); |
| } |
| |
| /*! |
| Returns a painted version of the QDeclarativeTextPrivate::layout QTextLayout. |
| If \a drawStyle is true, the style color overrides all colors in the document. |
| */ |
| QPixmap QDeclarativeTextPrivate::textLayoutImage(bool drawStyle) |
| { |
| //do layout |
| QSize size = layedOutTextRect.size(); |
| //paint text |
| QPixmap img(size); |
| if (!size.isEmpty()) { |
| img.fill(Qt::transparent); |
| #ifdef Q_WS_MAC |
| bool oldSmooth = qt_applefontsmoothing_enabled; |
| qt_applefontsmoothing_enabled = false; |
| #endif |
| QPainter p(&img); |
| #ifdef Q_WS_MAC |
| qt_applefontsmoothing_enabled = oldSmooth; |
| #endif |
| drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle); |
| } |
| return img; |
| } |
| |
| /*! |
| Paints the QDeclarativeTextPrivate::layout QTextLayout into \a painter at \a pos. If |
| \a drawStyle is true, the style color overrides all colors in the document. |
| */ |
| void QDeclarativeTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle) |
| { |
| if (drawStyle) |
| painter->setPen(styleColor); |
| else |
| painter->setPen(color); |
| painter->setFont(font); |
| layout.draw(painter, pos); |
| if (!elidePos.isNull()) |
| painter->drawText(pos + elidePos, elideChar); |
| } |
| |
| /*! |
| Returns a painted version of the QDeclarativeTextPrivate::doc QTextDocument. |
| If \a drawStyle is true, the style color overrides all colors in the document. |
| */ |
| QPixmap QDeclarativeTextPrivate::textDocumentImage(bool drawStyle) |
| { |
| QSize size = doc->size().toSize(); |
| |
| //paint text |
| QPixmap img(size); |
| img.fill(Qt::transparent); |
| #ifdef Q_WS_MAC |
| bool oldSmooth = qt_applefontsmoothing_enabled; |
| qt_applefontsmoothing_enabled = false; |
| #endif |
| QPainter p(&img); |
| #ifdef Q_WS_MAC |
| qt_applefontsmoothing_enabled = oldSmooth; |
| #endif |
| |
| QAbstractTextDocumentLayout::PaintContext context; |
| |
| QTextOption oldOption(doc->defaultTextOption()); |
| if (drawStyle) { |
| context.palette.setColor(QPalette::Text, styleColor); |
| QTextOption colorOption(doc->defaultTextOption()); |
| colorOption.setFlags(QTextOption::SuppressColors); |
| doc->setDefaultTextOption(colorOption); |
| } else { |
| context.palette.setColor(QPalette::Text, color); |
| } |
| doc->documentLayout()->draw(&p, context); |
| if (drawStyle) |
| doc->setDefaultTextOption(oldOption); |
| return img; |
| } |
| |
| /*! |
| Mark the image cache as dirty. |
| */ |
| void QDeclarativeTextPrivate::invalidateImageCache() |
| { |
| Q_Q(QDeclarativeText); |
| |
| if(cacheAllTextAsImage || style != QDeclarativeText::Normal){//If actually using the image cache |
| if (imageCacheDirty) |
| return; |
| |
| imageCacheDirty = true; |
| imageCache = QPixmap(); |
| } |
| if (q->isComponentComplete()) |
| q->update(); |
| } |
| |
| /*! |
| Tests if the image cache is dirty, and repaints it if it is. |
| */ |
| void QDeclarativeTextPrivate::checkImageCache() |
| { |
| if (!imageCacheDirty) |
| return; |
| |
| if (text.isEmpty()) { |
| |
| imageCache = QPixmap(); |
| |
| } else { |
| |
| QPixmap textImage; |
| QPixmap styledImage; |
| |
| if (richText) { |
| textImage = textDocumentImage(false); |
| if (style != QDeclarativeText::Normal) |
| styledImage = textDocumentImage(true); //### should use styleColor |
| } else { |
| textImage = textLayoutImage(false); |
| if (style != QDeclarativeText::Normal) |
| styledImage = textLayoutImage(true); //### should use styleColor |
| } |
| |
| switch (style) { |
| case QDeclarativeText::Outline: |
| imageCache = drawOutline(textImage, styledImage); |
| break; |
| case QDeclarativeText::Sunken: |
| imageCache = drawOutline(textImage, styledImage, -1); |
| break; |
| case QDeclarativeText::Raised: |
| imageCache = drawOutline(textImage, styledImage, 1); |
| break; |
| default: |
| imageCache = textImage; |
| break; |
| } |
| |
| } |
| |
| imageCacheDirty = false; |
| } |
| |
| /*! |
| Ensures the QDeclarativeTextPrivate::doc variable is set to a valid text document |
| */ |
| void QDeclarativeTextPrivate::ensureDoc() |
| { |
| if (!doc) { |
| Q_Q(QDeclarativeText); |
| doc = new QTextDocumentWithImageResources(q); |
| doc->setDocumentMargin(0); |
| } |
| } |
| |
| /*! |
| Draw \a styleSource as an outline around \a source and return the new image. |
| */ |
| QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource) |
| { |
| QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); |
| img.fill(Qt::transparent); |
| |
| QPainter ppm(&img); |
| |
| QPoint pos(0, 0); |
| pos += QPoint(-1, 0); |
| ppm.drawPixmap(pos, styleSource); |
| pos += QPoint(2, 0); |
| ppm.drawPixmap(pos, styleSource); |
| pos += QPoint(-1, -1); |
| ppm.drawPixmap(pos, styleSource); |
| pos += QPoint(0, 2); |
| ppm.drawPixmap(pos, styleSource); |
| |
| pos += QPoint(0, -1); |
| ppm.drawPixmap(pos, source); |
| ppm.end(); |
| |
| return img; |
| } |
| |
| /*! |
| Draw \a styleSource below \a source at \a yOffset and return the new image. |
| */ |
| QPixmap QDeclarativeTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset) |
| { |
| QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2); |
| img.fill(Qt::transparent); |
| |
| QPainter ppm(&img); |
| |
| ppm.drawPixmap(QPoint(0, yOffset), styleSource); |
| ppm.drawPixmap(0, 0, source); |
| |
| ppm.end(); |
| |
| return img; |
| } |
| |
| /*! |
| \qmlclass Text QDeclarativeText |
| \ingroup qml-basic-visual-elements |
| \since 4.7 |
| \brief The Text item allows you to add formatted text to a scene. |
| \inherits Item |
| |
| Text items can display both plain and rich text. For example, red text with |
| a specific font and size can be defined like this: |
| |
| \qml |
| Text { |
| text: "Hello World!" |
| font.family: "Helvetica" |
| font.pointSize: 24 |
| color: "red" |
| } |
| \endqml |
| |
| Rich text is defined using HTML-style markup: |
| |
| \qml |
| Text { |
| text: "<b>Hello</b> <i>World!</i>" |
| } |
| \endqml |
| |
| \image declarative-text.png |
| |
| If height and width are not explicitly set, Text will attempt to determine how |
| much room is needed and set it accordingly. Unless \l wrapMode is set, it will always |
| prefer width to height (all text will be placed on a single line). |
| |
| The \l elide property can alternatively be used to fit a single line of |
| plain text to a set width. |
| |
| Note that the \l{Supported HTML Subset} is limited. Also, if the text contains |
| HTML img tags that load remote images, the text is reloaded. |
| |
| Text provides read-only text. For editable text, see \l TextEdit. |
| |
| \sa {declarative/text/fonts}{Fonts example} |
| */ |
| QDeclarativeText::QDeclarativeText(QDeclarativeItem *parent) |
| : QDeclarativeImplicitSizeItem(*(new QDeclarativeTextPrivate), parent) |
| { |
| } |
| |
| QDeclarativeText::~QDeclarativeText() |
| { |
| } |
| |
| /*! |
| \qmlproperty bool Text::clip |
| This property holds whether the text is clipped. |
| |
| Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. |
| |
| If you want to display potentially long text in a limited space, you probably want to use \c elide instead. |
| */ |
| |
| /*! |
| \qmlproperty bool Text::smooth |
| |
| This property holds whether the text is smoothly scaled or transformed. |
| |
| Smooth filtering gives better visual quality, but is slower. If |
| the item is displayed at its natural size, this property has no visual or |
| performance effect. |
| |
| \note Generally scaling artifacts are only visible if the item is stationary on |
| the screen. A common pattern when animating an item is to disable smooth |
| filtering at the beginning of the animation and reenable it at the conclusion. |
| */ |
| |
| /*! |
| \qmlsignal Text::onLinkActivated(string link) |
| |
| This handler is called when the user clicks on a link embedded in the text. |
| The link must be in rich text or HTML format and the |
| \a link string provides access to the particular link. |
| |
| \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0 |
| |
| The example code will display the text |
| "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}." |
| |
| Clicking on the highlighted link will output |
| \tt{http://qt.nokia.com link activated} to the console. |
| */ |
| |
| /*! |
| \qmlproperty string Text::font.family |
| |
| Sets the family name of the font. |
| |
| The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". |
| If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. |
| If the family isn't available a family will be set using the font matching algorithm. |
| */ |
| |
| /*! |
| \qmlproperty bool Text::font.bold |
| |
| Sets whether the font weight is bold. |
| */ |
| |
| /*! |
| \qmlproperty enumeration Text::font.weight |
| |
| Sets the font's weight. |
| |
| The weight can be one of: |
| \list |
| \o Font.Light |
| \o Font.Normal - the default |
| \o Font.DemiBold |
| \o Font.Bold |
| \o Font.Black |
| \endlist |
| |
| \qml |
| Text { text: "Hello"; font.weight: Font.DemiBold } |
| \endqml |
| */ |
| |
| /*! |
| \qmlproperty bool Text::font.italic |
| |
| Sets whether the font has an italic style. |
| */ |
| |
| /*! |
| \qmlproperty bool Text::font.underline |
| |
| Sets whether the text is underlined. |
| */ |
| |
| /*! |
| \qmlproperty bool Text::font.strikeout |
| |
| Sets whether the font has a strikeout style. |
| */ |
| |
| /*! |
| \qmlproperty real Text::font.pointSize |
| |
| Sets the font size in points. The point size must be greater than zero. |
| */ |
| |
| /*! |
| \qmlproperty int Text::font.pixelSize |
| |
| Sets the font size in pixels. |
| |
| Using this function makes the font device dependent. |
| Use \c pointSize to set the size of the font in a device independent manner. |
| */ |
| |
| /*! |
| \qmlproperty real Text::font.letterSpacing |
| |
| Sets the letter spacing for the font. |
| |
| Letter spacing changes the default spacing between individual letters in the font. |
| A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. |
| */ |
| |
| /*! |
| \qmlproperty real Text::font.wordSpacing |
| |
| Sets the word spacing for the font. |
| |
| Word spacing changes the default spacing between individual words. |
| A positive value increases the word spacing by a corresponding amount of pixels, |
| while a negative value decreases the inter-word spacing accordingly. |
| */ |
| |
| /*! |
| \qmlproperty enumeration Text::font.capitalization |
| |
| Sets the capitalization for the text. |
| |
| \list |
| \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. |
| \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. |
| \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. |
| \o Font.SmallCaps - This alters the text to be rendered in small-caps type. |
| \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. |
| \endlist |
| |
| \qml |
| Text { text: "Hello"; font.capitalization: Font.AllLowercase } |
| \endqml |
| */ |
| QFont QDeclarativeText::font() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->sourceFont; |
| } |
| |
| void QDeclarativeText::setFont(const QFont &font) |
| { |
| Q_D(QDeclarativeText); |
| if (d->sourceFont == font) |
| return; |
| |
| d->sourceFont = font; |
| QFont oldFont = d->font; |
| d->font = font; |
| if (d->font.pointSizeF() != -1) { |
| // 0.5pt resolution |
| qreal size = qRound(d->font.pointSizeF()*2.0); |
| d->font.setPointSizeF(size/2.0); |
| } |
| |
| if (oldFont != d->font) |
| d->updateLayout(); |
| |
| emit fontChanged(d->sourceFont); |
| } |
| |
| /*! |
| \qmlproperty string Text::text |
| |
| The text to display. Text supports both plain and rich text strings. |
| |
| The item will try to automatically determine whether the text should |
| be treated as rich text. This determination is made using Qt::mightBeRichText(). |
| */ |
| QString QDeclarativeText::text() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->text; |
| } |
| |
| void QDeclarativeText::setText(const QString &n) |
| { |
| Q_D(QDeclarativeText); |
| if (d->text == n) |
| return; |
| |
| d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n)); |
| d->text = n; |
| if (isComponentComplete()) { |
| if (d->richText) { |
| d->ensureDoc(); |
| d->doc->setText(n); |
| d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); |
| } else { |
| d->rightToLeftText = d->text.isRightToLeft(); |
| } |
| d->determineHorizontalAlignment(); |
| } |
| d->updateLayout(); |
| emit textChanged(d->text); |
| } |
| |
| |
| /*! |
| \qmlproperty color Text::color |
| |
| The text color. |
| |
| An example of green text defined using hexadecimal notation: |
| \qml |
| Text { |
| color: "#00FF00" |
| text: "green text" |
| } |
| \endqml |
| |
| An example of steel blue text defined using an SVG color name: |
| \qml |
| Text { |
| color: "steelblue" |
| text: "blue text" |
| } |
| \endqml |
| */ |
| QColor QDeclarativeText::color() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->color; |
| } |
| |
| void QDeclarativeText::setColor(const QColor &color) |
| { |
| Q_D(QDeclarativeText); |
| if (d->color == color) |
| return; |
| |
| d->color = color; |
| d->invalidateImageCache(); |
| emit colorChanged(d->color); |
| } |
| |
| /*! |
| \qmlproperty enumeration Text::style |
| |
| Set an additional text style. |
| |
| Supported text styles are: |
| \list |
| \o Text.Normal - the default |
| \o Text.Outline |
| \o Text.Raised |
| \o Text.Sunken |
| \endlist |
| |
| \qml |
| Row { |
| Text { font.pointSize: 24; text: "Normal" } |
| Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" } |
| Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" } |
| Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" } |
| } |
| \endqml |
| |
| \image declarative-textstyle.png |
| */ |
| QDeclarativeText::TextStyle QDeclarativeText::style() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->style; |
| } |
| |
| void QDeclarativeText::setStyle(QDeclarativeText::TextStyle style) |
| { |
| Q_D(QDeclarativeText); |
| if (d->style == style) |
| return; |
| |
| // changing to/from Normal requires the boundingRect() to change |
| if (isComponentComplete() && (d->style == Normal || style == Normal)) |
| prepareGeometryChange(); |
| d->style = style; |
| d->invalidateImageCache(); |
| emit styleChanged(d->style); |
| } |
| |
| /*! |
| \qmlproperty color Text::styleColor |
| |
| Defines the secondary color used by text styles. |
| |
| \c styleColor is used as the outline color for outlined text, and as the |
| shadow color for raised or sunken text. If no style has been set, it is not |
| used at all. |
| |
| \qml |
| Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" } |
| \endqml |
| |
| \sa style |
| */ |
| QColor QDeclarativeText::styleColor() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->styleColor; |
| } |
| |
| void QDeclarativeText::setStyleColor(const QColor &color) |
| { |
| Q_D(QDeclarativeText); |
| if (d->styleColor == color) |
| return; |
| |
| d->styleColor = color; |
| d->invalidateImageCache(); |
| emit styleColorChanged(d->styleColor); |
| } |
| |
| |
| /*! |
| \qmlproperty enumeration Text::horizontalAlignment |
| \qmlproperty enumeration Text::verticalAlignment |
| |
| Sets the horizontal and vertical alignment of the text within the Text items |
| width and height. By default, the text is vertically aligned to the top. Horizontal |
| alignment follows the natural alignment of the text, for example text that is read |
| from left to right will be aligned to the left. |
| |
| The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and |
| \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom |
| and \c Text.AlignVCenter. |
| |
| Note that for a single line of text, the size of the text is the area of the text. In this common case, |
| all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will |
| need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to |
| that of the parent. |
| |
| When using the attached property \l {LayoutMirroring::enabled} to mirror application |
| layouts, the horizontal alignment of text will also be mirrored. However, the property |
| \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment |
| of Text, use the property \l {LayoutMirroring::enabled}. |
| */ |
| QDeclarativeText::HAlignment QDeclarativeText::hAlign() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->hAlign; |
| } |
| |
| void QDeclarativeText::setHAlign(HAlignment align) |
| { |
| Q_D(QDeclarativeText); |
| bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; |
| d->hAlignImplicit = false; |
| if (d->setHAlign(align, forceAlign) && isComponentComplete()) |
| d->updateLayout(); |
| } |
| |
| void QDeclarativeText::resetHAlign() |
| { |
| Q_D(QDeclarativeText); |
| d->hAlignImplicit = true; |
| if (d->determineHorizontalAlignment() && isComponentComplete()) |
| d->updateLayout(); |
| } |
| |
| QDeclarativeText::HAlignment QDeclarativeText::effectiveHAlign() const |
| { |
| Q_D(const QDeclarativeText); |
| QDeclarativeText::HAlignment effectiveAlignment = d->hAlign; |
| if (!d->hAlignImplicit && d->effectiveLayoutMirror) { |
| switch (d->hAlign) { |
| case QDeclarativeText::AlignLeft: |
| effectiveAlignment = QDeclarativeText::AlignRight; |
| break; |
| case QDeclarativeText::AlignRight: |
| effectiveAlignment = QDeclarativeText::AlignLeft; |
| break; |
| default: |
| break; |
| } |
| } |
| return effectiveAlignment; |
| } |
| |
| bool QDeclarativeTextPrivate::setHAlign(QDeclarativeText::HAlignment alignment, bool forceAlign) |
| { |
| Q_Q(QDeclarativeText); |
| if (hAlign != alignment || forceAlign) { |
| QDeclarativeText::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); |
| hAlign = alignment; |
| emit q->horizontalAlignmentChanged(hAlign); |
| return true; |
| } |
| return false; |
| } |
| |
| bool QDeclarativeTextPrivate::determineHorizontalAlignment() |
| { |
| Q_Q(QDeclarativeText); |
| if (hAlignImplicit && q->isComponentComplete()) { |
| bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; |
| return setHAlign(alignToRight ? QDeclarativeText::AlignRight : QDeclarativeText::AlignLeft); |
| } |
| return false; |
| } |
| |
| void QDeclarativeTextPrivate::mirrorChange() |
| { |
| Q_Q(QDeclarativeText); |
| if (q->isComponentComplete()) { |
| if (!hAlignImplicit && (hAlign == QDeclarativeText::AlignRight || hAlign == QDeclarativeText::AlignLeft)) { |
| updateLayout(); |
| } |
| } |
| } |
| |
| QTextDocument *QDeclarativeTextPrivate::textDocument() |
| { |
| return doc; |
| } |
| |
| QDeclarativeText::VAlignment QDeclarativeText::vAlign() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->vAlign; |
| } |
| |
| void QDeclarativeText::setVAlign(VAlignment align) |
| { |
| Q_D(QDeclarativeText); |
| if (d->vAlign == align) |
| return; |
| |
| if (isComponentComplete()) |
| prepareGeometryChange(); |
| d->vAlign = align; |
| emit verticalAlignmentChanged(align); |
| } |
| |
| /*! |
| \qmlproperty enumeration Text::wrapMode |
| |
| Set this property to wrap the text to the Text item's width. The text will only |
| wrap if an explicit width has been set. wrapMode can be one of: |
| |
| \list |
| \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width. |
| \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width. |
| \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. |
| \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. |
| \endlist |
| */ |
| QDeclarativeText::WrapMode QDeclarativeText::wrapMode() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->wrapMode; |
| } |
| |
| void QDeclarativeText::setWrapMode(WrapMode mode) |
| { |
| Q_D(QDeclarativeText); |
| if (mode == d->wrapMode) |
| return; |
| |
| d->wrapMode = mode; |
| d->updateLayout(); |
| |
| emit wrapModeChanged(); |
| } |
| |
| /*! |
| \qmlproperty int Text::lineCount |
| \since QtQuick 1.1 |
| |
| Returns the number of lines visible in the text item. |
| |
| This property is not supported for rich text. |
| |
| \sa maximumLineCount |
| */ |
| int QDeclarativeText::lineCount() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->lineCount; |
| } |
| |
| /*! |
| \qmlproperty bool Text::truncated |
| \since QtQuick 1.1 |
| |
| Returns true if the text has been truncated due to \l maximumLineCount |
| or \l elide. |
| |
| This property is not supported for rich text. |
| |
| \sa maximumLineCount, elide |
| */ |
| bool QDeclarativeText::truncated() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->truncated; |
| } |
| |
| /*! |
| \qmlproperty int Text::maximumLineCount |
| \since QtQuick 1.1 |
| |
| Set this property to limit the number of lines that the text item will show. |
| If elide is set to Text.ElideRight, the text will be elided appropriately. |
| By default, this is the value of the largest possible integer. |
| |
| This property is not supported for rich text. |
| |
| \sa lineCount, elide |
| */ |
| int QDeclarativeText::maximumLineCount() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->maximumLineCount; |
| } |
| |
| void QDeclarativeText::setMaximumLineCount(int lines) |
| { |
| Q_D(QDeclarativeText); |
| |
| d->maximumLineCountValid = lines==INT_MAX ? false : true; |
| if (d->maximumLineCount != lines) { |
| d->maximumLineCount = lines; |
| d->updateLayout(); |
| emit maximumLineCountChanged(); |
| } |
| } |
| |
| void QDeclarativeText::resetMaximumLineCount() |
| { |
| Q_D(QDeclarativeText); |
| setMaximumLineCount(INT_MAX); |
| d->elidePos = QPointF(); |
| if (d->truncated != false) { |
| d->truncated = false; |
| emit truncatedChanged(); |
| } |
| } |
| |
| /*! |
| \qmlproperty enumeration Text::textFormat |
| |
| The way the text property should be displayed. |
| |
| Supported text formats are: |
| |
| \list |
| \o Text.AutoText (default) |
| \o Text.PlainText |
| \o Text.RichText |
| \o Text.StyledText |
| \endlist |
| |
| If the text format is \c Text.AutoText the text element |
| will automatically determine whether the text should be treated as |
| rich text. This determination is made using Qt::mightBeRichText(). |
| |
| Text.StyledText is an optimized format supporting some basic text |
| styling markup, in the style of html 3.2: |
| |
| \code |
| <font size="4" color="#ff0000">font size and color</font> |
| <b>bold</b> |
| <i>italic</i> |
| <br> |
| > < & |
| \endcode |
| |
| \c Text.StyledText parser is strict, requiring tags to be correctly nested. |
| |
| \table |
| \row |
| \o |
| \qml |
| Column { |
| Text { |
| font.pointSize: 24 |
| text: "<b>Hello</b> <i>World!</i>" |
| } |
| Text { |
| font.pointSize: 24 |
| textFormat: Text.RichText |
| text: "<b>Hello</b> <i>World!</i>" |
| } |
| Text { |
| font.pointSize: 24 |
| textFormat: Text.PlainText |
| text: "<b>Hello</b> <i>World!</i>" |
| } |
| } |
| \endqml |
| \o \image declarative-textformat.png |
| \endtable |
| */ |
| QDeclarativeText::TextFormat QDeclarativeText::textFormat() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->format; |
| } |
| |
| void QDeclarativeText::setTextFormat(TextFormat format) |
| { |
| Q_D(QDeclarativeText); |
| if (format == d->format) |
| return; |
| d->format = format; |
| bool wasRich = d->richText; |
| d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); |
| |
| if (!wasRich && d->richText && isComponentComplete()) { |
| d->ensureDoc(); |
| d->doc->setText(d->text); |
| } |
| |
| d->updateLayout(); |
| |
| emit textFormatChanged(d->format); |
| } |
| |
| /*! |
| \qmlproperty enumeration Text::elide |
| |
| Set this property to elide parts of the text fit to the Text item's width. |
| The text will only elide if an explicit width has been set. |
| |
| This property cannot be used with rich text. |
| |
| Eliding can be: |
| \list |
| \o Text.ElideNone - the default |
| \o Text.ElideLeft |
| \o Text.ElideMiddle |
| \o Text.ElideRight |
| \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. |
| |
| 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. |
| |
| Multi-length strings are ordered from longest to shortest, separated by the |
| Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}). |
| */ |
| QDeclarativeText::TextElideMode QDeclarativeText::elideMode() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->elideMode; |
| } |
| |
| void QDeclarativeText::setElideMode(QDeclarativeText::TextElideMode mode) |
| { |
| Q_D(QDeclarativeText); |
| if (mode == d->elideMode) |
| return; |
| |
| d->elideMode = mode; |
| d->updateLayout(); |
| |
| emit elideModeChanged(d->elideMode); |
| } |
| |
| /*! \internal */ |
| QRectF QDeclarativeText::boundingRect() const |
| { |
| Q_D(const QDeclarativeText); |
| |
| QRect rect = d->layedOutTextRect; |
| if (d->style != Normal) |
| rect.adjust(-1, 0, 1, 2); |
| |
| // Could include font max left/right bearings to either side of rectangle. |
| |
| int h = height(); |
| switch (d->vAlign) { |
| case AlignTop: |
| break; |
| case AlignBottom: |
| rect.moveTop(h - rect.height()); |
| break; |
| case AlignVCenter: |
| rect.moveTop((h - rect.height()) / 2); |
| break; |
| } |
| |
| return QRectF(rect); |
| } |
| |
| /*! \internal */ |
| void QDeclarativeText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
| { |
| Q_D(QDeclarativeText); |
| if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) |
| && (d->wrapMode != QDeclarativeText::NoWrap |
| || d->elideMode != QDeclarativeText::ElideNone |
| || d->hAlign != QDeclarativeText::AlignLeft)) { |
| if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QDeclarativeText::ElideNone && widthValid()) { |
| // We need to re-elide |
| d->updateLayout(); |
| } else { |
| // We just need to re-layout |
| d->updateSize(); |
| } |
| } |
| |
| QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); |
| } |
| |
| /*! |
| \qmlproperty real Text::paintedWidth |
| |
| Returns the width of the text, including width past the width |
| which is covered due to insufficient wrapping if WrapMode is set. |
| */ |
| qreal QDeclarativeText::paintedWidth() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->paintedSize.width(); |
| } |
| |
| /*! |
| \qmlproperty real Text::paintedHeight |
| |
| Returns the height of the text, including height past the height |
| which is covered due to there being more text than fits in the set height. |
| */ |
| qreal QDeclarativeText::paintedHeight() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->paintedSize.height(); |
| } |
| |
| /*! |
| \qmlproperty real Text::lineHeight |
| \since QtQuick 1.1 |
| |
| Sets the line height for the text. |
| The value can be in pixels or a multiplier depending on lineHeightMode. |
| |
| The default value is a multiplier of 1.0. |
| The line height must be a positive value. |
| */ |
| qreal QDeclarativeText::lineHeight() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->lineHeight; |
| } |
| |
| void QDeclarativeText::setLineHeight(qreal lineHeight) |
| { |
| Q_D(QDeclarativeText); |
| |
| if ((d->lineHeight == lineHeight) || (lineHeight < 0.0)) |
| return; |
| |
| d->lineHeight = lineHeight; |
| d->updateLayout(); |
| emit lineHeightChanged(lineHeight); |
| } |
| |
| /*! |
| \qmlproperty enumeration Text::lineHeightMode |
| |
| This property determines how the line height is specified. |
| The possible values are: |
| |
| \list |
| \o Text.ProportionalHeight (default) - this sets the spacing proportional to the |
| line (as a multiplier). For example, set to 2 for double spacing. |
| \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels). |
| \endlist |
| */ |
| QDeclarativeText::LineHeightMode QDeclarativeText::lineHeightMode() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->lineHeightMode; |
| } |
| |
| void QDeclarativeText::setLineHeightMode(LineHeightMode mode) |
| { |
| Q_D(QDeclarativeText); |
| if (mode == d->lineHeightMode) |
| return; |
| |
| d->lineHeightMode = mode; |
| d->updateLayout(); |
| |
| emit lineHeightModeChanged(mode); |
| } |
| |
| /*! |
| Returns the number of resources (images) that are being loaded asynchronously. |
| */ |
| int QDeclarativeText::resourcesLoading() const |
| { |
| Q_D(const QDeclarativeText); |
| return d->doc ? d->doc->resourcesLoading() : 0; |
| } |
| |
| /*! \internal */ |
| void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) |
| { |
| Q_D(QDeclarativeText); |
| |
| if (d->cacheAllTextAsImage || d->style != Normal) { |
| d->checkImageCache(); |
| if (d->imageCache.isNull()) |
| return; |
| |
| bool oldAA = p->testRenderHint(QPainter::Antialiasing); |
| bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); |
| if (d->smooth) |
| p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); |
| |
| QRect br = boundingRect().toRect(); |
| |
| bool needClip = clip() && (d->imageCache.width() > width() || |
| d->imageCache.height() > height()); |
| |
| if (needClip) |
| p->drawPixmap(0, 0, width(), height(), d->imageCache, -br.x(), -br.y(), width(), height()); |
| else |
| p->drawPixmap(br.x(), br.y(), d->imageCache); |
| |
| if (d->smooth) { |
| p->setRenderHint(QPainter::Antialiasing, oldAA); |
| p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); |
| } |
| } else { |
| QRectF bounds = boundingRect(); |
| |
| bool needClip = clip() && (d->layedOutTextRect.width() > width() || |
| d->layedOutTextRect.height() > height()); |
| |
| if (needClip) { |
| p->save(); |
| p->setClipRect(0, 0, width(), height(), Qt::IntersectClip); |
| } |
| if (d->richText) { |
| QAbstractTextDocumentLayout::PaintContext context; |
| context.palette.setColor(QPalette::Text, d->color); |
| p->translate(bounds.x(), bounds.y()); |
| d->doc->documentLayout()->draw(p, context); |
| p->translate(-bounds.x(), -bounds.y()); |
| } else { |
| d->drawTextLayout(p, QPointF(0, bounds.y()), false); |
| } |
| |
| if (needClip) { |
| p->restore(); |
| } |
| } |
| } |
| |
| /*! \internal */ |
| void QDeclarativeText::componentComplete() |
| { |
| Q_D(QDeclarativeText); |
| QDeclarativeItem::componentComplete(); |
| if (d->updateOnComponentComplete) { |
| d->updateOnComponentComplete = false; |
| if (d->richText) { |
| d->ensureDoc(); |
| d->doc->setText(d->text); |
| d->rightToLeftText = d->doc->toPlainText().isRightToLeft(); |
| } else { |
| d->rightToLeftText = d->text.isRightToLeft(); |
| } |
| d->determineHorizontalAlignment(); |
| d->updateLayout(); |
| } |
| } |
| |
| /*! \internal */ |
| void QDeclarativeText::mousePressEvent(QGraphicsSceneMouseEvent *event) |
| { |
| Q_D(QDeclarativeText); |
| |
| if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) { |
| event->setAccepted(false); |
| d->activeLink.clear(); |
| } else { |
| d->activeLink = d->doc->documentLayout()->anchorAt(event->pos()); |
| } |
| |
| // ### may malfunction if two of the same links are clicked & dragged onto each other) |
| |
| if (!event->isAccepted()) |
| QDeclarativeItem::mousePressEvent(event); |
| |
| } |
| |
| /*! \internal */ |
| void QDeclarativeText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
| { |
| Q_D(QDeclarativeText); |
| |
| // ### confirm the link, and send a signal out |
| if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos())) |
| emit linkActivated(d->activeLink); |
| else |
| event->setAccepted(false); |
| |
| if (!event->isAccepted()) |
| QDeclarativeItem::mouseReleaseEvent(event); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "qdeclarativetext.moc" |