1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgtext_p.h"
43 #include "qsgtext_p_p.h"
45 #include <private/qsgdistancefieldglyphcache_p.h>
46 #include <private/qsgcontext_p.h>
47 #include <private/qsgadaptationlayer_p.h>
48 #include "qsgtextnode_p.h"
49 #include "qsgimage_p_p.h"
50 #include <private/qsgtexture_p.h>
52 #include <QtDeclarative/qdeclarativeinfo.h>
53 #include <QtGui/qgraphicssceneevent.h>
54 #include <QtGui/qabstracttextdocumentlayout.h>
55 #include <QtGui/qpainter.h>
56 #include <QtGui/qtextdocument.h>
57 #include <QtGui/qtextobject.h>
58 #include <QtGui/qtextcursor.h>
59 #include <QtGui/qapplication.h>
61 #include <private/qdeclarativestyledtext_p.h>
62 #include <private/qdeclarativepixmapcache_p.h>
69 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
71 class QSGTextDocumentWithImageResources : public QTextDocument {
75 QSGTextDocumentWithImageResources(QSGText *parent);
76 virtual ~QSGTextDocumentWithImageResources();
78 void setText(const QString &);
79 int resourcesLoading() const { return outstanding; }
82 QVariant loadResource(int type, const QUrl &name);
85 void requestFinished();
88 QHash<QUrl, QDeclarativePixmap *> m_resources;
91 static QSet<QUrl> errors;
94 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
95 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
97 QString QSGTextPrivate::elideChar = QString(0x2026);
99 QSGTextPrivate::QSGTextPrivate()
100 : color((QRgb)0), style(QSGText::Normal), hAlign(QSGText::AlignLeft),
101 vAlign(QSGText::AlignTop), elideMode(QSGText::ElideNone),
102 format(QSGText::AutoText), wrapMode(QSGText::NoWrap), lineHeight(1),
103 lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
104 maximumLineCountValid(false),
106 imageCacheDirty(false), updateOnComponentComplete(true),
107 richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
108 requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
109 layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
110 doc(0), nodeType(NodeIsNull)
112 #if defined(Q_OS_MAC)
117 cacheAllTextAsImage = enableImageCache();
120 void QSGTextPrivate::init()
123 q->setAcceptedMouseButtons(Qt::LeftButton);
124 q->setFlag(QSGItem::ItemHasContents);
127 QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
128 : QTextDocument(parent), outstanding(0)
130 setUndoRedoEnabled(false);
133 QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
135 if (!m_resources.isEmpty())
136 qDeleteAll(m_resources);
139 QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
141 QDeclarativeContext *context = qmlContext(parent());
142 QUrl url = context->resolvedUrl(name);
144 if (type == QTextDocument::ImageResource) {
145 QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
147 if (iter == m_resources.end()) {
148 QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
149 iter = m_resources.insert(name, p);
151 if (p->isLoading()) {
152 p->connectFinished(this, SLOT(requestFinished()));
157 QDeclarativePixmap *p = *iter;
160 } else if (p->isError()) {
161 if (!errors.contains(url)) {
163 qmlInfo(parent()) << p->error();
168 return QTextDocument::loadResource(type,url); // The *resolved* URL
171 void QSGTextDocumentWithImageResources::requestFinished()
174 if (outstanding == 0) {
175 QSGText *textItem = static_cast<QSGText*>(parent());
176 QString text = textItem->text();
177 #ifndef QT_NO_TEXTHTMLPARSER
182 QSGTextPrivate *d = QSGTextPrivate::get(textItem);
187 void QSGTextDocumentWithImageResources::setText(const QString &text)
189 if (!m_resources.isEmpty()) {
190 qDeleteAll(m_resources);
195 #ifndef QT_NO_TEXTHTMLPARSER
202 QSet<QUrl> QSGTextDocumentWithImageResources::errors;
204 QSGTextPrivate::~QSGTextPrivate()
208 qreal QSGTextPrivate::getImplicitWidth() const
210 if (!requireImplicitWidth) {
211 // We don't calculate implicitWidth unless it is required.
212 // We need to force a size update now to ensure implicitWidth is calculated
213 QSGTextPrivate *me = const_cast<QSGTextPrivate*>(this);
214 me->requireImplicitWidth = true;
217 return implicitWidth;
220 void QSGTextPrivate::updateLayout()
223 if (!q->isComponentComplete()) {
224 updateOnComponentComplete = true;
228 layoutTextElided = false;
229 // Setup instance of QTextLayout for all cases other than richtext
231 layout.clearLayout();
232 layout.setFont(font);
233 if (format != QSGText::StyledText) {
235 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
236 singleline = !tmp.contains(QChar::LineSeparator);
237 if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) {
238 QFontMetrics fm(font);
239 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
241 layoutTextElided = true;
244 emit q->truncatedChanged();
251 QDeclarativeStyledText::parse(text, layout);
255 QTextBlockFormat::LineHeightTypes type;
256 type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
257 QTextBlockFormat blockFormat;
258 blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type);
259 for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
260 QTextCursor cursor(it);
261 cursor.setBlockFormat(blockFormat);
268 void QSGTextPrivate::updateSize()
272 if (!q->isComponentComplete()) {
273 updateOnComponentComplete = true;
277 if (!requireImplicitWidth) {
278 emit q->implicitWidthChanged();
279 // if the implicitWidth is used, then updateSize() has already been called (recursively)
280 if (requireImplicitWidth)
284 invalidateImageCache();
286 QFontMetrics fm(font);
287 if (text.isEmpty()) {
288 q->setImplicitWidth(0);
289 q->setImplicitHeight(fm.height());
290 paintedSize = QSize(0, fm.height());
291 emit q->paintedSizeChanged();
296 int dy = q->height();
299 #if defined(Q_OS_MAC)
300 layoutThread = QThread::currentThread();
303 //setup instance of QTextLayout for all cases other than richtext
305 QRect textRect = setupTextLayout();
306 layedOutTextRect = textRect;
307 size = textRect.size();
310 singleline = false; // richtext can't elide or be optimized for single-line case
312 doc->setDefaultFont(font);
313 QSGText::HAlignment horizontalAlignment = q->effectiveHAlign();
314 if (rightToLeftText) {
315 if (horizontalAlignment == QSGText::AlignLeft)
316 horizontalAlignment = QSGText::AlignRight;
317 else if (horizontalAlignment == QSGText::AlignRight)
318 horizontalAlignment = QSGText::AlignLeft;
321 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
322 option.setWrapMode(QTextOption::WrapMode(wrapMode));
323 if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
324 option.setUseDesignMetrics(true);
325 doc->setDefaultTextOption(option);
326 if (requireImplicitWidth && q->widthValid()) {
327 doc->setTextWidth(-1);
328 naturalWidth = doc->idealWidth();
330 if (wrapMode != QSGText::NoWrap && q->widthValid())
331 doc->setTextWidth(q->width());
333 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
334 dy -= (int)doc->size().height();
335 QSize dsize = doc->size().toSize();
336 layedOutTextRect = QRect(QPoint(0,0), dsize);
337 size = QSize(int(doc->idealWidth()),dsize.height());
341 if (q->heightValid()) {
342 if (vAlign == QSGText::AlignBottom)
344 else if (vAlign == QSGText::AlignVCenter)
347 q->setBaselineOffset(fm.ascent() + yoff);
349 //### need to comfirm cost of always setting these for richText
350 internalWidthUpdate = true;
351 if (!q->widthValid())
352 q->setImplicitWidth(size.width());
353 else if (requireImplicitWidth)
354 q->setImplicitWidth(naturalWidth);
355 internalWidthUpdate = false;
357 q->setImplicitHeight(size.height());
358 if (paintedSize != size) {
360 emit q->paintedSizeChanged();
366 Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
368 Returns the size of the final text. This can be used to position the text vertically (the text is
369 already absolutely positioned horizontally).
371 QRect QSGTextPrivate::setupTextLayout()
373 // ### text layout handling should be profiled and optimized as needed
374 // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
376 layout.setCacheEnabled(true);
379 int visibleCount = 0;
383 lineWidth = q->width();
385 QTextOption textOption = layout.textOption();
386 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
387 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
388 if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
389 textOption.setUseDesignMetrics(true);
390 layout.setTextOption(textOption);
392 bool elideText = false;
393 bool truncate = false;
395 QFontMetrics fm(layout.font());
396 elidePos = QPointF();
398 if (requireImplicitWidth && q->widthValid()) {
399 // requires an extra layout
401 if (layoutTextElided) {
402 // We have provided elided text to the layout, but we must calculate unelided width.
403 elidedText = layout.text();
404 layout.setText(text);
406 layout.beginLayout();
408 QTextLine line = layout.createLine();
414 for (int i = 0; i < layout.lineCount(); ++i) {
415 QTextLine line = layout.lineAt(i);
416 br = br.united(line.naturalTextRect());
418 naturalWidth = br.width();
419 if (layoutTextElided)
420 layout.setText(elidedText);
423 if (maximumLineCountValid) {
424 layout.beginLayout();
427 int linesLeft = maximumLineCount;
428 int visibleTextLength = 0;
429 while (linesLeft > 0) {
430 QTextLine line = layout.createLine();
436 line.setLineWidth(lineWidth);
437 visibleTextLength += line.textLength();
439 if (--linesLeft == 0) {
440 if (visibleTextLength < text.length()) {
442 if (elideMode==QSGText::ElideRight && q->widthValid()) {
443 qreal elideWidth = fm.width(elideChar);
444 // Need to correct for alignment
445 line.setLineWidth(lineWidth-elideWidth);
446 if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
447 line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
448 elidePos.setX(line.naturalTextRect().left() - elideWidth);
450 elidePos.setX(line.naturalTextRect().right());
460 if (truncated != truncate) {
461 truncated = truncate;
462 emit q->truncatedChanged();
465 layout.beginLayout();
467 QTextLine line = layout.createLine();
472 line.setLineWidth(lineWidth);
479 for (int i = 0; i < layout.lineCount(); ++i) {
480 QTextLine line = layout.lineAt(i);
482 line.setPosition(QPointF(line.position().x(), height));
483 if (elideText && i == layout.lineCount()-1) {
484 elidePos.setY(height + fm.ascent());
485 br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
487 br = br.united(line.naturalTextRect());
488 height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
490 br.setHeight(height);
492 if (!q->widthValid())
493 naturalWidth = br.width();
495 //Update the number of visible lines
496 if (lineCount != visibleCount) {
497 lineCount = visibleCount;
498 emit q->lineCountChanged();
501 return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
505 Returns a painted version of the QSGTextPrivate::layout QTextLayout.
506 If \a drawStyle is true, the style color overrides all colors in the document.
508 QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
510 QSize size = layedOutTextRect.size();
514 if (!size.isEmpty()) {
515 img.fill(Qt::transparent);
517 bool oldSmooth = qt_applefontsmoothing_enabled;
518 qt_applefontsmoothing_enabled = false;
522 qt_applefontsmoothing_enabled = oldSmooth;
524 drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
530 Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos. If
531 \a drawStyle is true, the style color overrides all colors in the document.
533 void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
536 painter->setPen(styleColor);
538 painter->setPen(color);
539 painter->setFont(font);
540 layout.draw(painter, pos);
541 if (!elidePos.isNull())
542 painter->drawText(pos + elidePos, elideChar);
546 Returns a painted version of the QSGTextPrivate::doc QTextDocument.
547 If \a drawStyle is true, the style color overrides all colors in the document.
549 QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
551 QSize size = doc->size().toSize();
555 img.fill(Qt::transparent);
557 bool oldSmooth = qt_applefontsmoothing_enabled;
558 qt_applefontsmoothing_enabled = false;
562 qt_applefontsmoothing_enabled = oldSmooth;
565 QAbstractTextDocumentLayout::PaintContext context;
567 QTextOption oldOption(doc->defaultTextOption());
569 context.palette.setColor(QPalette::Text, styleColor);
570 QTextOption colorOption(doc->defaultTextOption());
571 colorOption.setFlags(QTextOption::SuppressColors);
572 doc->setDefaultTextOption(colorOption);
574 context.palette.setColor(QPalette::Text, color);
576 doc->documentLayout()->draw(&p, context);
578 doc->setDefaultTextOption(oldOption);
583 Mark the image cache as dirty.
585 void QSGTextPrivate::invalidateImageCache()
589 if(richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QSGText::Normal)){//If actually using the image cache
593 imageCacheDirty = true;
595 if (q->isComponentComplete())
596 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
597 } else if (q->isComponentComplete())
602 Tests if the image cache is dirty, and repaints it if it is.
604 void QSGTextPrivate::checkImageCache()
608 if (!imageCacheDirty)
611 if (text.isEmpty()) {
613 imageCache = QPixmap();
621 textImage = textDocumentImage(false);
622 if (style != QSGText::Normal)
623 styledImage = textDocumentImage(true); //### should use styleColor
625 textImage = textLayoutImage(false);
626 if (style != QSGText::Normal)
627 styledImage = textLayoutImage(true); //### should use styleColor
631 case QSGText::Outline:
632 imageCache = drawOutline(textImage, styledImage);
634 case QSGText::Sunken:
635 imageCache = drawOutline(textImage, styledImage, -1);
637 case QSGText::Raised:
638 imageCache = drawOutline(textImage, styledImage, 1);
641 imageCache = textImage;
647 imageCacheDirty = false;
648 textureImageCacheDirty = true;
653 Ensures the QSGTextPrivate::doc variable is set to a valid text document
655 void QSGTextPrivate::ensureDoc()
659 doc = new QSGTextDocumentWithImageResources(q);
660 doc->setDocumentMargin(0);
665 Draw \a styleSource as an outline around \a source and return the new image.
667 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
669 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
670 img.fill(Qt::transparent);
675 pos += QPoint(-1, 0);
676 ppm.drawPixmap(pos, styleSource);
678 ppm.drawPixmap(pos, styleSource);
679 pos += QPoint(-1, -1);
680 ppm.drawPixmap(pos, styleSource);
682 ppm.drawPixmap(pos, styleSource);
684 pos += QPoint(0, -1);
685 ppm.drawPixmap(pos, source);
692 Draw \a styleSource below \a source at \a yOffset and return the new image.
694 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
696 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
697 img.fill(Qt::transparent);
701 ppm.drawPixmap(QPoint(0, yOffset), styleSource);
702 ppm.drawPixmap(0, 0, source);
710 \qmlclass Text QSGText
711 \inqmlmodule QtQuick 2
712 \ingroup qml-basic-visual-elements
713 \brief The Text item allows you to add formatted text to a scene.
716 Text items can display both plain and rich text. For example, red text with
717 a specific font and size can be defined like this:
722 font.family: "Helvetica"
728 Rich text is defined using HTML-style markup:
732 text: "<b>Hello</b> <i>World!</i>"
736 \image declarative-text.png
738 If height and width are not explicitly set, Text will attempt to determine how
739 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
740 prefer width to height (all text will be placed on a single line).
742 The \l elide property can alternatively be used to fit a single line of
743 plain text to a set width.
745 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
746 HTML img tags that load remote images, the text is reloaded.
748 Text provides read-only text. For editable text, see \l TextEdit.
750 \sa {declarative/text/fonts}{Fonts example}
752 QSGText::QSGText(QSGItem *parent)
753 : QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
764 \qmlproperty bool QtQuick2::Text::clip
765 This property holds whether the text is clipped.
767 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
769 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
773 \qmlproperty bool QtQuick2::Text::smooth
775 This property holds whether the text is smoothly scaled or transformed.
777 Smooth filtering gives better visual quality, but is slower. If
778 the item is displayed at its natural size, this property has no visual or
781 \note Generally scaling artifacts are only visible if the item is stationary on
782 the screen. A common pattern when animating an item is to disable smooth
783 filtering at the beginning of the animation and reenable it at the conclusion.
787 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
789 This handler is called when the user clicks on a link embedded in the text.
790 The link must be in rich text or HTML format and the
791 \a link string provides access to the particular link.
793 \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
795 The example code will display the text
796 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
798 Clicking on the highlighted link will output
799 \tt{http://qt.nokia.com link activated} to the console.
803 \qmlproperty string QtQuick2::Text::font.family
805 Sets the family name of the font.
807 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
808 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
809 If the family isn't available a family will be set using the font matching algorithm.
813 \qmlproperty bool QtQuick2::Text::font.bold
815 Sets whether the font weight is bold.
819 \qmlproperty enumeration QtQuick2::Text::font.weight
821 Sets the font's weight.
823 The weight can be one of:
826 \o Font.Normal - the default
833 Text { text: "Hello"; font.weight: Font.DemiBold }
838 \qmlproperty bool QtQuick2::Text::font.italic
840 Sets whether the font has an italic style.
844 \qmlproperty bool QtQuick2::Text::font.underline
846 Sets whether the text is underlined.
850 \qmlproperty bool QtQuick2::Text::font.strikeout
852 Sets whether the font has a strikeout style.
856 \qmlproperty real QtQuick2::Text::font.pointSize
858 Sets the font size in points. The point size must be greater than zero.
862 \qmlproperty int QtQuick2::Text::font.pixelSize
864 Sets the font size in pixels.
866 Using this function makes the font device dependent.
867 Use \c pointSize to set the size of the font in a device independent manner.
871 \qmlproperty real QtQuick2::Text::font.letterSpacing
873 Sets the letter spacing for the font.
875 Letter spacing changes the default spacing between individual letters in the font.
876 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
880 \qmlproperty real QtQuick2::Text::font.wordSpacing
882 Sets the word spacing for the font.
884 Word spacing changes the default spacing between individual words.
885 A positive value increases the word spacing by a corresponding amount of pixels,
886 while a negative value decreases the inter-word spacing accordingly.
890 \qmlproperty enumeration QtQuick2::Text::font.capitalization
892 Sets the capitalization for the text.
895 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
896 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
897 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
898 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
899 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
903 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
906 QFont QSGText::font() const
909 return d->sourceFont;
912 void QSGText::setFont(const QFont &font)
915 if (d->sourceFont == font)
918 d->sourceFont = font;
919 QFont oldFont = d->font;
922 if (d->font.pointSizeF() != -1) {
924 qreal size = qRound(d->font.pointSizeF()*2.0);
925 d->font.setPointSizeF(size/2.0);
928 if (oldFont != d->font)
931 emit fontChanged(d->sourceFont);
935 \qmlproperty string QtQuick2::Text::text
937 The text to display. Text supports both plain and rich text strings.
939 The item will try to automatically determine whether the text should
940 be treated as rich text. This determination is made using Qt::mightBeRichText().
942 QString QSGText::text() const
948 void QSGText::setText(const QString &n)
954 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
956 if (isComponentComplete()) {
960 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
961 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
963 d->rightToLeftText = d->text.isRightToLeft();
965 d->determineHorizontalAlignment();
968 emit textChanged(d->text);
972 \qmlproperty color QtQuick2::Text::color
976 An example of green text defined using hexadecimal notation:
984 An example of steel blue text defined using an SVG color name:
992 QColor QSGText::color() const
998 void QSGText::setColor(const QColor &color)
1001 if (d->color == color)
1005 d->invalidateImageCache();
1006 emit colorChanged(d->color);
1009 \qmlproperty enumeration QtQuick2::Text::style
1011 Set an additional text style.
1013 Supported text styles are:
1015 \o Text.Normal - the default
1023 Text { font.pointSize: 24; text: "Normal" }
1024 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1025 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1026 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1030 \image declarative-textstyle.png
1032 QSGText::TextStyle QSGText::style() const
1038 void QSGText::setStyle(QSGText::TextStyle style)
1041 if (d->style == style)
1044 // changing to/from Normal requires the boundingRect() to change
1045 if (isComponentComplete() && (d->style == Normal || style == Normal))
1048 d->invalidateImageCache();
1049 emit styleChanged(d->style);
1053 \qmlproperty color QtQuick2::Text::styleColor
1055 Defines the secondary color used by text styles.
1057 \c styleColor is used as the outline color for outlined text, and as the
1058 shadow color for raised or sunken text. If no style has been set, it is not
1062 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1067 QColor QSGText::styleColor() const
1070 return d->styleColor;
1073 void QSGText::setStyleColor(const QColor &color)
1076 if (d->styleColor == color)
1079 d->styleColor = color;
1080 d->invalidateImageCache();
1081 emit styleColorChanged(d->styleColor);
1085 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1086 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1087 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1089 Sets the horizontal and vertical alignment of the text within the Text items
1090 width and height. By default, the text is vertically aligned to the top. Horizontal
1091 alignment follows the natural alignment of the text, for example text that is read
1092 from left to right will be aligned to the left.
1094 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1095 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1096 and \c Text.AlignVCenter.
1098 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1099 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1100 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1103 When using the attached property LayoutMirroring::enabled to mirror application
1104 layouts, the horizontal alignment of text will also be mirrored. However, the property
1105 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1106 of Text, use the read-only property \c effectiveHorizontalAlignment.
1108 QSGText::HAlignment QSGText::hAlign() const
1114 void QSGText::setHAlign(HAlignment align)
1117 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1118 d->hAlignImplicit = false;
1119 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1123 void QSGText::resetHAlign()
1126 d->hAlignImplicit = true;
1127 if (d->determineHorizontalAlignment() && isComponentComplete())
1131 QSGText::HAlignment QSGText::effectiveHAlign() const
1134 QSGText::HAlignment effectiveAlignment = d->hAlign;
1135 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1136 switch (d->hAlign) {
1137 case QSGText::AlignLeft:
1138 effectiveAlignment = QSGText::AlignRight;
1140 case QSGText::AlignRight:
1141 effectiveAlignment = QSGText::AlignLeft;
1147 return effectiveAlignment;
1150 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1153 if (hAlign != alignment || forceAlign) {
1154 QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1157 emit q->horizontalAlignmentChanged(hAlign);
1158 if (oldEffectiveHAlign != q->effectiveHAlign())
1159 emit q->effectiveHorizontalAlignmentChanged();
1165 bool QSGTextPrivate::determineHorizontalAlignment()
1168 if (hAlignImplicit && q->isComponentComplete()) {
1169 bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1170 return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1175 void QSGTextPrivate::mirrorChange()
1178 if (q->isComponentComplete()) {
1179 if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1181 emit q->effectiveHorizontalAlignmentChanged();
1186 QTextDocument *QSGTextPrivate::textDocument()
1191 QSGText::VAlignment QSGText::vAlign() const
1197 void QSGText::setVAlign(VAlignment align)
1200 if (d->vAlign == align)
1204 emit verticalAlignmentChanged(align);
1208 \qmlproperty enumeration QtQuick2::Text::wrapMode
1210 Set this property to wrap the text to the Text item's width. The text will only
1211 wrap if an explicit width has been set. wrapMode can be one of:
1214 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1215 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1216 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1217 \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.
1220 QSGText::WrapMode QSGText::wrapMode() const
1226 void QSGText::setWrapMode(WrapMode mode)
1229 if (mode == d->wrapMode)
1235 emit wrapModeChanged();
1239 \qmlproperty int QtQuick2::Text::lineCount
1241 Returns the number of lines visible in the text item.
1243 This property is not supported for rich text.
1245 \sa maximumLineCount
1247 int QSGText::lineCount() const
1250 return d->lineCount;
1254 \qmlproperty bool QtQuick2::Text::truncated
1256 Returns true if the text has been truncated due to \l maximumLineCount
1259 This property is not supported for rich text.
1261 \sa maximumLineCount, elide
1263 bool QSGText::truncated() const
1266 return d->truncated;
1270 \qmlproperty int QtQuick2::Text::maximumLineCount
1272 Set this property to limit the number of lines that the text item will show.
1273 If elide is set to Text.ElideRight, the text will be elided appropriately.
1274 By default, this is the value of the largest possible integer.
1276 This property is not supported for rich text.
1278 \sa lineCount, elide
1280 int QSGText::maximumLineCount() const
1283 return d->maximumLineCount;
1286 void QSGText::setMaximumLineCount(int lines)
1290 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1291 if (d->maximumLineCount != lines) {
1292 d->maximumLineCount = lines;
1294 emit maximumLineCountChanged();
1298 void QSGText::resetMaximumLineCount()
1301 setMaximumLineCount(INT_MAX);
1302 d->elidePos = QPointF();
1303 if (d->truncated != false) {
1304 d->truncated = false;
1305 emit truncatedChanged();
1310 \qmlproperty enumeration QtQuick2::Text::textFormat
1312 The way the text property should be displayed.
1314 Supported text formats are:
1317 \o Text.AutoText (default)
1323 If the text format is \c Text.AutoText the text element
1324 will automatically determine whether the text should be treated as
1325 rich text. This determination is made using Qt::mightBeRichText().
1327 Text.StyledText is an optimized format supporting some basic text
1328 styling markup, in the style of html 3.2:
1331 <font size="4" color="#ff0000">font size and color</font>
1338 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1347 text: "<b>Hello</b> <i>World!</i>"
1351 textFormat: Text.RichText
1352 text: "<b>Hello</b> <i>World!</i>"
1356 textFormat: Text.PlainText
1357 text: "<b>Hello</b> <i>World!</i>"
1361 \o \image declarative-textformat.png
1364 QSGText::TextFormat QSGText::textFormat() const
1370 void QSGText::setTextFormat(TextFormat format)
1373 if (format == d->format)
1376 bool wasRich = d->richText;
1377 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1379 if (!wasRich && d->richText && isComponentComplete()) {
1381 d->doc->setText(d->text);
1382 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1387 emit textFormatChanged(d->format);
1391 \qmlproperty enumeration QtQuick2::Text::elide
1393 Set this property to elide parts of the text fit to the Text item's width.
1394 The text will only elide if an explicit width has been set.
1396 This property cannot be used with rich text.
1400 \o Text.ElideNone - the default
1406 If this property is set to Text.ElideRight, it can be used with multiline
1407 text. The text will only elide if maximumLineCount has been set.
1409 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1410 the first string that fits will be used, otherwise the last will be elided.
1412 Multi-length strings are ordered from longest to shortest, separated by the
1413 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1415 QSGText::TextElideMode QSGText::elideMode() const
1418 return d->elideMode;
1421 void QSGText::setElideMode(QSGText::TextElideMode mode)
1424 if (mode == d->elideMode)
1427 d->elideMode = mode;
1430 emit elideModeChanged(d->elideMode);
1434 QRectF QSGText::boundingRect() const
1438 QRect rect = d->layedOutTextRect;
1439 if (d->style != Normal)
1440 rect.adjust(-1, 0, 1, 2);
1442 // Could include font max left/right bearings to either side of rectangle.
1445 switch (d->vAlign) {
1449 rect.moveTop(h - rect.height());
1452 rect.moveTop((h - rect.height()) / 2);
1456 return QRectF(rect);
1460 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1463 if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1464 && (d->wrapMode != QSGText::NoWrap
1465 || d->elideMode != QSGText::ElideNone
1466 || d->hAlign != QSGText::AlignLeft)) {
1467 if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1468 // We need to re-elide
1471 // We just need to re-layout
1476 QSGItem::geometryChanged(newGeometry, oldGeometry);
1479 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1484 if (d->text.isEmpty()) {
1489 QRectF bounds = boundingRect();
1491 // We need to make sure the layout is done in the current thread
1492 #if defined(Q_OS_MAC)
1493 if (d->layoutThread != QThread::currentThread())
1497 // XXX todo - some styled text can be done by the QSGTextNode
1498 if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1499 bool wasDirty = d->textureImageCacheDirty;
1500 d->textureImageCacheDirty = false;
1502 if (d->imageCache.isNull()) {
1507 QSGImageNode *node = 0;
1508 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1510 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1511 d->texture = new QSGPlainTexture();
1513 d->nodeType = QSGTextPrivate::NodeIsTexture;
1515 node = static_cast<QSGImageNode *>(oldNode);
1516 Q_ASSERT(d->texture);
1520 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1521 node->setTexture(0);
1522 node->setTexture(d->texture);
1525 node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1526 node->setSourceRect(QRectF(0, 0, 1, 1));
1527 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1528 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1529 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1535 QSGTextNode *node = 0;
1536 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1538 node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1539 d->nodeType = QSGTextPrivate::NodeIsText;
1541 node = static_cast<QSGTextNode *>(oldNode);
1544 node->deleteContent();
1545 node->setMatrix(QMatrix4x4());
1550 node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1553 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1560 bool QSGText::event(QEvent *e)
1563 if (e->type() == QEvent::User) {
1564 d->checkImageCache();
1567 return QSGImplicitSizeItem::event(e);
1572 \qmlproperty real QtQuick2::Text::paintedWidth
1574 Returns the width of the text, including width past the width
1575 which is covered due to insufficient wrapping if WrapMode is set.
1577 qreal QSGText::paintedWidth() const
1580 return d->paintedSize.width();
1584 \qmlproperty real QtQuick2::Text::paintedHeight
1586 Returns the height of the text, including height past the height
1587 which is covered due to there being more text than fits in the set height.
1589 qreal QSGText::paintedHeight() const
1592 return d->paintedSize.height();
1596 \qmlproperty real QtQuick2::Text::lineHeight
1598 Sets the line height for the text.
1599 The value can be in pixels or a multiplier depending on lineHeightMode.
1601 The default value is a multiplier of 1.0.
1602 The line height must be a positive value.
1604 qreal QSGText::lineHeight() const
1607 return d->lineHeight;
1610 void QSGText::setLineHeight(qreal lineHeight)
1614 if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1617 d->lineHeight = lineHeight;
1619 emit lineHeightChanged(lineHeight);
1623 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1625 This property determines how the line height is specified.
1626 The possible values are:
1629 \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1630 line (as a multiplier). For example, set to 2 for double spacing.
1631 \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1634 QSGText::LineHeightMode QSGText::lineHeightMode() const
1637 return d->lineHeightMode;
1640 void QSGText::setLineHeightMode(LineHeightMode mode)
1643 if (mode == d->lineHeightMode)
1646 d->lineHeightMode = mode;
1649 emit lineHeightModeChanged(mode);
1653 Returns the number of resources (images) that are being loaded asynchronously.
1655 int QSGText::resourcesLoading() const
1658 return d->doc ? d->doc->resourcesLoading() : 0;
1662 void QSGText::componentComplete()
1665 QSGItem::componentComplete();
1666 if (d->updateOnComponentComplete) {
1667 d->updateOnComponentComplete = false;
1670 d->doc->setText(d->text);
1671 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1672 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1674 d->rightToLeftText = d->text.isRightToLeft();
1676 d->determineHorizontalAlignment();
1682 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1686 if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1687 event->setAccepted(false);
1688 d->activeLink.clear();
1690 d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1693 // ### may malfunction if two of the same links are clicked & dragged onto each other)
1695 if (!event->isAccepted())
1696 QSGItem::mousePressEvent(event);
1701 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1705 // ### confirm the link, and send a signal out
1706 if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1707 emit linkActivated(d->activeLink);
1709 event->setAccepted(false);
1711 if (!event->isAccepted())
1712 QSGItem::mouseReleaseEvent(event);
1717 #include "qsgtext.moc"