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/qevent.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/qguiapplication.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), styledText(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);
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 styled 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;
955 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
957 if (isComponentComplete()) {
961 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
962 d->richTextAsImage = enableImageCache();
964 d->rightToLeftText = d->text.isRightToLeft();
966 d->determineHorizontalAlignment();
969 emit textChanged(d->text);
973 \qmlproperty color QtQuick2::Text::color
977 An example of green text defined using hexadecimal notation:
985 An example of steel blue text defined using an SVG color name:
993 QColor QSGText::color() const
999 void QSGText::setColor(const QColor &color)
1002 if (d->color == color)
1006 d->invalidateImageCache();
1007 emit colorChanged(d->color);
1010 \qmlproperty enumeration QtQuick2::Text::style
1012 Set an additional text style.
1014 Supported text styles are:
1016 \o Text.Normal - the default
1024 Text { font.pointSize: 24; text: "Normal" }
1025 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1026 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1027 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1031 \image declarative-textstyle.png
1033 QSGText::TextStyle QSGText::style() const
1039 void QSGText::setStyle(QSGText::TextStyle style)
1042 if (d->style == style)
1045 // changing to/from Normal requires the boundingRect() to change
1046 if (isComponentComplete() && (d->style == Normal || style == Normal))
1049 d->invalidateImageCache();
1050 emit styleChanged(d->style);
1054 \qmlproperty color QtQuick2::Text::styleColor
1056 Defines the secondary color used by text styles.
1058 \c styleColor is used as the outline color for outlined text, and as the
1059 shadow color for raised or sunken text. If no style has been set, it is not
1063 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1068 QColor QSGText::styleColor() const
1071 return d->styleColor;
1074 void QSGText::setStyleColor(const QColor &color)
1077 if (d->styleColor == color)
1080 d->styleColor = color;
1081 d->invalidateImageCache();
1082 emit styleColorChanged(d->styleColor);
1086 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1087 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1088 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1090 Sets the horizontal and vertical alignment of the text within the Text items
1091 width and height. By default, the text is vertically aligned to the top. Horizontal
1092 alignment follows the natural alignment of the text, for example text that is read
1093 from left to right will be aligned to the left.
1095 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1096 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1097 and \c Text.AlignVCenter.
1099 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1100 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1101 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1104 When using the attached property LayoutMirroring::enabled to mirror application
1105 layouts, the horizontal alignment of text will also be mirrored. However, the property
1106 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1107 of Text, use the read-only property \c effectiveHorizontalAlignment.
1109 QSGText::HAlignment QSGText::hAlign() const
1115 void QSGText::setHAlign(HAlignment align)
1118 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1119 d->hAlignImplicit = false;
1120 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1124 void QSGText::resetHAlign()
1127 d->hAlignImplicit = true;
1128 if (d->determineHorizontalAlignment() && isComponentComplete())
1132 QSGText::HAlignment QSGText::effectiveHAlign() const
1135 QSGText::HAlignment effectiveAlignment = d->hAlign;
1136 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1137 switch (d->hAlign) {
1138 case QSGText::AlignLeft:
1139 effectiveAlignment = QSGText::AlignRight;
1141 case QSGText::AlignRight:
1142 effectiveAlignment = QSGText::AlignLeft;
1148 return effectiveAlignment;
1151 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1154 if (hAlign != alignment || forceAlign) {
1155 QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1158 emit q->horizontalAlignmentChanged(hAlign);
1159 if (oldEffectiveHAlign != q->effectiveHAlign())
1160 emit q->effectiveHorizontalAlignmentChanged();
1166 bool QSGTextPrivate::determineHorizontalAlignment()
1169 if (hAlignImplicit && q->isComponentComplete()) {
1170 bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1171 return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1176 void QSGTextPrivate::mirrorChange()
1179 if (q->isComponentComplete()) {
1180 if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1182 emit q->effectiveHorizontalAlignmentChanged();
1187 QTextDocument *QSGTextPrivate::textDocument()
1192 QSGText::VAlignment QSGText::vAlign() const
1198 void QSGText::setVAlign(VAlignment align)
1201 if (d->vAlign == align)
1205 emit verticalAlignmentChanged(align);
1209 \qmlproperty enumeration QtQuick2::Text::wrapMode
1211 Set this property to wrap the text to the Text item's width. The text will only
1212 wrap if an explicit width has been set. wrapMode can be one of:
1215 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1216 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1217 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1218 \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.
1221 QSGText::WrapMode QSGText::wrapMode() const
1227 void QSGText::setWrapMode(WrapMode mode)
1230 if (mode == d->wrapMode)
1236 emit wrapModeChanged();
1240 \qmlproperty int QtQuick2::Text::lineCount
1242 Returns the number of lines visible in the text item.
1244 This property is not supported for rich text.
1246 \sa maximumLineCount
1248 int QSGText::lineCount() const
1251 return d->lineCount;
1255 \qmlproperty bool QtQuick2::Text::truncated
1257 Returns true if the text has been truncated due to \l maximumLineCount
1260 This property is not supported for rich text.
1262 \sa maximumLineCount, elide
1264 bool QSGText::truncated() const
1267 return d->truncated;
1271 \qmlproperty int QtQuick2::Text::maximumLineCount
1273 Set this property to limit the number of lines that the text item will show.
1274 If elide is set to Text.ElideRight, the text will be elided appropriately.
1275 By default, this is the value of the largest possible integer.
1277 This property is not supported for rich text.
1279 \sa lineCount, elide
1281 int QSGText::maximumLineCount() const
1284 return d->maximumLineCount;
1287 void QSGText::setMaximumLineCount(int lines)
1291 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1292 if (d->maximumLineCount != lines) {
1293 d->maximumLineCount = lines;
1295 emit maximumLineCountChanged();
1299 void QSGText::resetMaximumLineCount()
1302 setMaximumLineCount(INT_MAX);
1303 d->elidePos = QPointF();
1304 if (d->truncated != false) {
1305 d->truncated = false;
1306 emit truncatedChanged();
1311 \qmlproperty enumeration QtQuick2::Text::textFormat
1313 The way the text property should be displayed.
1315 Supported text formats are:
1318 \o Text.AutoText (default)
1324 If the text format is \c Text.AutoText the text element
1325 will automatically determine whether the text should be treated as
1326 styled text. This determination is made using Qt::mightBeRichText().
1328 Text.StyledText is an optimized format supporting some basic text
1329 styling markup, in the style of html 3.2:
1336 <u> - underlined text
1337 <font color="color_name" size="1-7"></font>
1338 <h1> to <h6> - headers
1339 <a href=""> - anchor
1340 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1344 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1353 text: "<b>Hello</b> <i>World!</i>"
1357 textFormat: Text.RichText
1358 text: "<b>Hello</b> <i>World!</i>"
1362 textFormat: Text.PlainText
1363 text: "<b>Hello</b> <i>World!</i>"
1367 \o \image declarative-textformat.png
1370 QSGText::TextFormat QSGText::textFormat() const
1376 void QSGText::setTextFormat(TextFormat format)
1379 if (format == d->format)
1382 bool wasRich = d->richText;
1383 d->richText = format == RichText;
1384 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1386 if (!wasRich && d->richText && isComponentComplete()) {
1388 d->doc->setText(d->text);
1389 d->richTextAsImage = enableImageCache();
1394 emit textFormatChanged(d->format);
1398 \qmlproperty enumeration QtQuick2::Text::elide
1400 Set this property to elide parts of the text fit to the Text item's width.
1401 The text will only elide if an explicit width has been set.
1403 This property cannot be used with rich text.
1407 \o Text.ElideNone - the default
1413 If this property is set to Text.ElideRight, it can be used with multiline
1414 text. The text will only elide if maximumLineCount has been set.
1416 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1417 the first string that fits will be used, otherwise the last will be elided.
1419 Multi-length strings are ordered from longest to shortest, separated by the
1420 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1422 QSGText::TextElideMode QSGText::elideMode() const
1425 return d->elideMode;
1428 void QSGText::setElideMode(QSGText::TextElideMode mode)
1431 if (mode == d->elideMode)
1434 d->elideMode = mode;
1437 emit elideModeChanged(d->elideMode);
1441 QRectF QSGText::boundingRect() const
1445 QRect rect = d->layedOutTextRect;
1446 if (d->style != Normal)
1447 rect.adjust(-1, 0, 1, 2);
1449 // Could include font max left/right bearings to either side of rectangle.
1452 switch (d->vAlign) {
1456 rect.moveTop(h - rect.height());
1459 rect.moveTop((h - rect.height()) / 2);
1463 return QRectF(rect);
1467 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1470 if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1471 && (d->wrapMode != QSGText::NoWrap
1472 || d->elideMode != QSGText::ElideNone
1473 || d->hAlign != QSGText::AlignLeft)) {
1474 if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1475 // We need to re-elide
1478 // We just need to re-layout
1483 QSGItem::geometryChanged(newGeometry, oldGeometry);
1486 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1491 if (d->text.isEmpty()) {
1496 QRectF bounds = boundingRect();
1498 // We need to make sure the layout is done in the current thread
1499 #if defined(Q_OS_MAC)
1500 if (d->layoutThread != QThread::currentThread())
1504 // XXX todo - some styled text can be done by the QSGTextNode
1505 if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1506 bool wasDirty = d->textureImageCacheDirty;
1507 d->textureImageCacheDirty = false;
1509 if (d->imageCache.isNull()) {
1514 QSGImageNode *node = 0;
1515 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1517 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1518 d->texture = new QSGPlainTexture();
1520 d->nodeType = QSGTextPrivate::NodeIsTexture;
1522 node = static_cast<QSGImageNode *>(oldNode);
1523 Q_ASSERT(d->texture);
1527 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1528 node->setTexture(0);
1529 node->setTexture(d->texture);
1532 node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1533 node->setSourceRect(QRectF(0, 0, 1, 1));
1534 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1535 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1536 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1542 QSGTextNode *node = 0;
1543 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1545 node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1546 d->nodeType = QSGTextPrivate::NodeIsText;
1548 node = static_cast<QSGTextNode *>(oldNode);
1551 node->deleteContent();
1552 node->setMatrix(QMatrix4x4());
1557 node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1560 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1567 bool QSGText::event(QEvent *e)
1570 if (e->type() == QEvent::User) {
1571 d->checkImageCache();
1574 return QSGImplicitSizeItem::event(e);
1579 \qmlproperty real QtQuick2::Text::paintedWidth
1581 Returns the width of the text, including width past the width
1582 which is covered due to insufficient wrapping if WrapMode is set.
1584 qreal QSGText::paintedWidth() const
1587 return d->paintedSize.width();
1591 \qmlproperty real QtQuick2::Text::paintedHeight
1593 Returns the height of the text, including height past the height
1594 which is covered due to there being more text than fits in the set height.
1596 qreal QSGText::paintedHeight() const
1599 return d->paintedSize.height();
1603 \qmlproperty real QtQuick2::Text::lineHeight
1605 Sets the line height for the text.
1606 The value can be in pixels or a multiplier depending on lineHeightMode.
1608 The default value is a multiplier of 1.0.
1609 The line height must be a positive value.
1611 qreal QSGText::lineHeight() const
1614 return d->lineHeight;
1617 void QSGText::setLineHeight(qreal lineHeight)
1621 if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1624 d->lineHeight = lineHeight;
1626 emit lineHeightChanged(lineHeight);
1630 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1632 This property determines how the line height is specified.
1633 The possible values are:
1636 \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1637 line (as a multiplier). For example, set to 2 for double spacing.
1638 \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1641 QSGText::LineHeightMode QSGText::lineHeightMode() const
1644 return d->lineHeightMode;
1647 void QSGText::setLineHeightMode(LineHeightMode mode)
1650 if (mode == d->lineHeightMode)
1653 d->lineHeightMode = mode;
1656 emit lineHeightModeChanged(mode);
1660 Returns the number of resources (images) that are being loaded asynchronously.
1662 int QSGText::resourcesLoading() const
1665 return d->doc ? d->doc->resourcesLoading() : 0;
1669 void QSGText::componentComplete()
1672 QSGItem::componentComplete();
1673 if (d->updateOnComponentComplete) {
1674 d->updateOnComponentComplete = false;
1677 d->doc->setText(d->text);
1678 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1679 d->richTextAsImage = enableImageCache();
1681 d->rightToLeftText = d->text.isRightToLeft();
1683 d->determineHorizontalAlignment();
1689 QString QSGTextPrivate::anchorAt(const QPointF &mousePos)
1692 for (int i = 0; i < layout.lineCount(); ++i) {
1693 QTextLine line = layout.lineAt(i);
1694 if (line.naturalTextRect().contains(mousePos)) {
1695 int charPos = line.xToCursor(mousePos.x());
1696 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
1697 if (formatRange.format.isAnchor()
1698 && charPos >= formatRange.start
1699 && charPos <= formatRange.start + formatRange.length) {
1700 return formatRange.format.anchorHref();
1710 bool QSGTextPrivate::isLinkActivatedConnected()
1712 static int idx = this->signalIndex("linkActivated(QString)");
1713 return this->isSignalConnected(idx);
1717 void QSGText::mousePressEvent(QMouseEvent *event)
1721 if (d->isLinkActivatedConnected()) {
1723 d->activeLink = d->anchorAt(event->localPos());
1724 else if (d->richText && d->doc)
1725 d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
1728 if (d->activeLink.isEmpty())
1729 event->setAccepted(false);
1731 // ### may malfunction if two of the same links are clicked & dragged onto each other)
1733 if (!event->isAccepted())
1734 QSGItem::mousePressEvent(event);
1739 void QSGText::mouseReleaseEvent(QMouseEvent *event)
1743 // ### confirm the link, and send a signal out
1746 if (d->isLinkActivatedConnected()) {
1748 link = d->anchorAt(event->localPos());
1749 else if (d->richText && d->doc)
1750 link = d->doc->documentLayout()->anchorAt(event->localPos());
1753 if (!link.isEmpty() && d->activeLink == link)
1754 emit linkActivated(d->activeLink);
1756 event->setAccepted(false);
1758 if (!event->isAccepted())
1759 QSGItem::mouseReleaseEvent(event);
1764 #include "qsgtext.moc"