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(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
96 QString QSGTextPrivate::elideChar = QString(0x2026);
98 QSGTextPrivate::QSGTextPrivate()
99 : color((QRgb)0), style(QSGText::Normal), hAlign(QSGText::AlignLeft),
100 vAlign(QSGText::AlignTop), elideMode(QSGText::ElideNone),
101 format(QSGText::AutoText), wrapMode(QSGText::NoWrap), lineHeight(1),
102 lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
103 maximumLineCountValid(false),
105 imageCacheDirty(false), updateOnComponentComplete(true),
106 richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
107 requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
108 layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
109 doc(0), layoutThread(0), nodeType(NodeIsNull)
111 cacheAllTextAsImage = enableImageCache();
114 void QSGTextPrivate::init()
117 q->setAcceptedMouseButtons(Qt::LeftButton);
118 q->setFlag(QSGItem::ItemHasContents);
121 QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
122 : QTextDocument(parent), outstanding(0)
124 setUndoRedoEnabled(false);
127 QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
129 if (!m_resources.isEmpty())
130 qDeleteAll(m_resources);
133 QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
135 QDeclarativeContext *context = qmlContext(parent());
136 QUrl url = context->resolvedUrl(name);
138 if (type == QTextDocument::ImageResource) {
139 QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
141 if (iter == m_resources.end()) {
142 QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
143 iter = m_resources.insert(name, p);
145 if (p->isLoading()) {
146 p->connectFinished(this, SLOT(requestFinished()));
151 QDeclarativePixmap *p = *iter;
154 } else if (p->isError()) {
155 if (!errors.contains(url)) {
157 qmlInfo(parent()) << p->error();
162 return QTextDocument::loadResource(type,url); // The *resolved* URL
165 void QSGTextDocumentWithImageResources::requestFinished()
168 if (outstanding == 0) {
169 QSGText *textItem = static_cast<QSGText*>(parent());
170 QString text = textItem->text();
171 #ifndef QT_NO_TEXTHTMLPARSER
176 QSGTextPrivate *d = QSGTextPrivate::get(textItem);
181 void QSGTextDocumentWithImageResources::setText(const QString &text)
183 if (!m_resources.isEmpty()) {
184 qDeleteAll(m_resources);
189 #ifndef QT_NO_TEXTHTMLPARSER
196 QSet<QUrl> QSGTextDocumentWithImageResources::errors;
198 QSGTextPrivate::~QSGTextPrivate()
202 qreal QSGTextPrivate::getImplicitWidth() const
204 if (!requireImplicitWidth) {
205 // We don't calculate implicitWidth unless it is required.
206 // We need to force a size update now to ensure implicitWidth is calculated
207 QSGTextPrivate *me = const_cast<QSGTextPrivate*>(this);
208 me->requireImplicitWidth = true;
211 return implicitWidth;
214 void QSGTextPrivate::updateLayout()
217 if (!q->isComponentComplete()) {
218 updateOnComponentComplete = true;
222 layoutTextElided = false;
223 // Setup instance of QTextLayout for all cases other than richtext
225 layout.clearLayout();
226 layout.setFont(font);
227 if (format != QSGText::StyledText) {
229 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
230 singleline = !tmp.contains(QChar::LineSeparator);
231 if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) {
232 QFontMetrics fm(font);
233 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
235 layoutTextElided = true;
238 emit q->truncatedChanged();
245 QDeclarativeStyledText::parse(text, layout);
249 QTextBlockFormat::LineHeightTypes type;
250 type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
251 QTextBlockFormat blockFormat;
252 blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type);
253 for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
254 QTextCursor cursor(it);
255 cursor.setBlockFormat(blockFormat);
262 void QSGTextPrivate::updateSize()
266 if (!q->isComponentComplete()) {
267 updateOnComponentComplete = true;
271 if (!requireImplicitWidth) {
272 emit q->implicitWidthChanged();
273 // if the implicitWidth is used, then updateSize() has already been called (recursively)
274 if (requireImplicitWidth)
278 invalidateImageCache();
280 QFontMetrics fm(font);
281 if (text.isEmpty()) {
282 q->setImplicitWidth(0);
283 q->setImplicitHeight(fm.height());
284 paintedSize = QSize(0, fm.height());
285 emit q->paintedSizeChanged();
290 int dy = q->height();
293 layoutThread = QThread::currentThread();
295 //setup instance of QTextLayout for all cases other than richtext
297 QRect textRect = setupTextLayout();
298 layedOutTextRect = textRect;
299 size = textRect.size();
302 singleline = false; // richtext can't elide or be optimized for single-line case
304 doc->setDefaultFont(font);
305 QSGText::HAlignment horizontalAlignment = q->effectiveHAlign();
306 if (rightToLeftText) {
307 if (horizontalAlignment == QSGText::AlignLeft)
308 horizontalAlignment = QSGText::AlignRight;
309 else if (horizontalAlignment == QSGText::AlignRight)
310 horizontalAlignment = QSGText::AlignLeft;
313 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
314 option.setWrapMode(QTextOption::WrapMode(wrapMode));
315 doc->setDefaultTextOption(option);
316 if (requireImplicitWidth && q->widthValid()) {
317 doc->setTextWidth(-1);
318 naturalWidth = doc->idealWidth();
320 if (wrapMode != QSGText::NoWrap && q->widthValid())
321 doc->setTextWidth(q->width());
323 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
324 dy -= (int)doc->size().height();
325 QSize dsize = doc->size().toSize();
326 layedOutTextRect = QRect(QPoint(0,0), dsize);
327 size = QSize(int(doc->idealWidth()),dsize.height());
331 if (q->heightValid()) {
332 if (vAlign == QSGText::AlignBottom)
334 else if (vAlign == QSGText::AlignVCenter)
337 q->setBaselineOffset(fm.ascent() + yoff);
339 //### need to comfirm cost of always setting these for richText
340 internalWidthUpdate = true;
341 if (!q->widthValid())
342 q->setImplicitWidth(size.width());
343 else if (requireImplicitWidth)
344 q->setImplicitWidth(naturalWidth);
345 internalWidthUpdate = false;
347 q->setImplicitHeight(size.height());
348 if (paintedSize != size) {
350 emit q->paintedSizeChanged();
356 Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
358 Returns the size of the final text. This can be used to position the text vertically (the text is
359 already absolutely positioned horizontally).
361 QRect QSGTextPrivate::setupTextLayout()
363 // ### text layout handling should be profiled and optimized as needed
364 // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
366 layout.setCacheEnabled(true);
369 int visibleCount = 0;
373 lineWidth = q->width();
375 QTextOption textOption = layout.textOption();
376 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
377 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
378 layout.setTextOption(textOption);
380 bool elideText = false;
381 bool truncate = false;
383 QFontMetrics fm(layout.font());
384 elidePos = QPointF();
386 if (requireImplicitWidth && q->widthValid()) {
387 // requires an extra layout
389 if (layoutTextElided) {
390 // We have provided elided text to the layout, but we must calculate unelided width.
391 elidedText = layout.text();
392 layout.setText(text);
394 layout.beginLayout();
396 QTextLine line = layout.createLine();
402 for (int i = 0; i < layout.lineCount(); ++i) {
403 QTextLine line = layout.lineAt(i);
404 br = br.united(line.naturalTextRect());
406 naturalWidth = br.width();
407 if (layoutTextElided)
408 layout.setText(elidedText);
411 if (maximumLineCountValid) {
412 layout.beginLayout();
415 int linesLeft = maximumLineCount;
416 int visibleTextLength = 0;
417 while (linesLeft > 0) {
418 QTextLine line = layout.createLine();
424 line.setLineWidth(lineWidth);
425 visibleTextLength += line.textLength();
427 if (--linesLeft == 0) {
428 if (visibleTextLength < text.length()) {
430 if (elideMode==QSGText::ElideRight && q->widthValid()) {
431 qreal elideWidth = fm.width(elideChar);
432 // Need to correct for alignment
433 line.setLineWidth(lineWidth-elideWidth);
434 if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
435 line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
436 elidePos.setX(line.naturalTextRect().left() - elideWidth);
438 elidePos.setX(line.naturalTextRect().right());
448 if (truncated != truncate) {
449 truncated = truncate;
450 emit q->truncatedChanged();
453 layout.beginLayout();
455 QTextLine line = layout.createLine();
460 line.setLineWidth(lineWidth);
467 for (int i = 0; i < layout.lineCount(); ++i) {
468 QTextLine line = layout.lineAt(i);
470 line.setPosition(QPointF(line.position().x(), height));
471 if (elideText && i == layout.lineCount()-1) {
472 elidePos.setY(height + fm.ascent());
473 br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
475 br = br.united(line.naturalTextRect());
476 height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
478 br.setHeight(height);
480 if (!q->widthValid())
481 naturalWidth = br.width();
483 //Update the number of visible lines
484 if (lineCount != visibleCount) {
485 lineCount = visibleCount;
486 emit q->lineCountChanged();
489 return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
493 Returns a painted version of the QSGTextPrivate::layout QTextLayout.
494 If \a drawStyle is true, the style color overrides all colors in the document.
496 QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
498 QSize size = layedOutTextRect.size();
502 if (!size.isEmpty()) {
503 img.fill(Qt::transparent);
505 bool oldSmooth = qt_applefontsmoothing_enabled;
506 qt_applefontsmoothing_enabled = false;
510 qt_applefontsmoothing_enabled = oldSmooth;
512 drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
518 Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos. If
519 \a drawStyle is true, the style color overrides all colors in the document.
521 void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
524 painter->setPen(styleColor);
526 painter->setPen(color);
527 painter->setFont(font);
528 layout.draw(painter, pos);
529 if (!elidePos.isNull())
530 painter->drawText(pos + elidePos, elideChar);
534 Returns a painted version of the QSGTextPrivate::doc QTextDocument.
535 If \a drawStyle is true, the style color overrides all colors in the document.
537 QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
539 QSize size = doc->size().toSize();
543 img.fill(Qt::transparent);
545 bool oldSmooth = qt_applefontsmoothing_enabled;
546 qt_applefontsmoothing_enabled = false;
550 qt_applefontsmoothing_enabled = oldSmooth;
553 QAbstractTextDocumentLayout::PaintContext context;
555 QTextOption oldOption(doc->defaultTextOption());
557 context.palette.setColor(QPalette::Text, styleColor);
558 QTextOption colorOption(doc->defaultTextOption());
559 colorOption.setFlags(QTextOption::SuppressColors);
560 doc->setDefaultTextOption(colorOption);
562 context.palette.setColor(QPalette::Text, color);
564 doc->documentLayout()->draw(&p, context);
566 doc->setDefaultTextOption(oldOption);
571 Mark the image cache as dirty.
573 void QSGTextPrivate::invalidateImageCache()
577 if(richTextAsImage || cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache
581 imageCacheDirty = true;
583 if (q->isComponentComplete())
584 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
585 } else if (q->isComponentComplete())
590 Tests if the image cache is dirty, and repaints it if it is.
592 void QSGTextPrivate::checkImageCache()
596 if (!imageCacheDirty)
599 if (text.isEmpty()) {
601 imageCache = QPixmap();
609 textImage = textDocumentImage(false);
610 if (style != QSGText::Normal)
611 styledImage = textDocumentImage(true); //### should use styleColor
613 textImage = textLayoutImage(false);
614 if (style != QSGText::Normal)
615 styledImage = textLayoutImage(true); //### should use styleColor
619 case QSGText::Outline:
620 imageCache = drawOutline(textImage, styledImage);
622 case QSGText::Sunken:
623 imageCache = drawOutline(textImage, styledImage, -1);
625 case QSGText::Raised:
626 imageCache = drawOutline(textImage, styledImage, 1);
629 imageCache = textImage;
635 imageCacheDirty = false;
636 textureImageCacheDirty = true;
641 Ensures the QSGTextPrivate::doc variable is set to a valid text document
643 void QSGTextPrivate::ensureDoc()
647 doc = new QSGTextDocumentWithImageResources(q);
648 doc->setDocumentMargin(0);
653 Draw \a styleSource as an outline around \a source and return the new image.
655 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
657 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
658 img.fill(Qt::transparent);
663 pos += QPoint(-1, 0);
664 ppm.drawPixmap(pos, styleSource);
666 ppm.drawPixmap(pos, styleSource);
667 pos += QPoint(-1, -1);
668 ppm.drawPixmap(pos, styleSource);
670 ppm.drawPixmap(pos, styleSource);
672 pos += QPoint(0, -1);
673 ppm.drawPixmap(pos, source);
680 Draw \a styleSource below \a source at \a yOffset and return the new image.
682 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
684 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
685 img.fill(Qt::transparent);
689 ppm.drawPixmap(QPoint(0, yOffset), styleSource);
690 ppm.drawPixmap(0, 0, source);
698 \qmlclass Text QSGText
699 \inqmlmodule QtQuick 2
700 \ingroup qml-basic-visual-elements
701 \brief The Text item allows you to add formatted text to a scene.
704 Text items can display both plain and rich text. For example, red text with
705 a specific font and size can be defined like this:
710 font.family: "Helvetica"
716 Rich text is defined using HTML-style markup:
720 text: "<b>Hello</b> <i>World!</i>"
724 \image declarative-text.png
726 If height and width are not explicitly set, Text will attempt to determine how
727 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
728 prefer width to height (all text will be placed on a single line).
730 The \l elide property can alternatively be used to fit a single line of
731 plain text to a set width.
733 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
734 HTML img tags that load remote images, the text is reloaded.
736 Text provides read-only text. For editable text, see \l TextEdit.
738 \sa {declarative/text/fonts}{Fonts example}
740 QSGText::QSGText(QSGItem *parent)
741 : QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
752 \qmlproperty bool QtQuick2::Text::clip
753 This property holds whether the text is clipped.
755 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
757 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
761 \qmlproperty bool QtQuick2::Text::smooth
763 This property holds whether the text is smoothly scaled or transformed.
765 Smooth filtering gives better visual quality, but is slower. If
766 the item is displayed at its natural size, this property has no visual or
769 \note Generally scaling artifacts are only visible if the item is stationary on
770 the screen. A common pattern when animating an item is to disable smooth
771 filtering at the beginning of the animation and reenable it at the conclusion.
775 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
777 This handler is called when the user clicks on a link embedded in the text.
778 The link must be in rich text or HTML format and the
779 \a link string provides access to the particular link.
781 \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
783 The example code will display the text
784 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
786 Clicking on the highlighted link will output
787 \tt{http://qt.nokia.com link activated} to the console.
791 \qmlproperty string QtQuick2::Text::font.family
793 Sets the family name of the font.
795 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
796 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
797 If the family isn't available a family will be set using the font matching algorithm.
801 \qmlproperty bool QtQuick2::Text::font.bold
803 Sets whether the font weight is bold.
807 \qmlproperty enumeration QtQuick2::Text::font.weight
809 Sets the font's weight.
811 The weight can be one of:
814 \o Font.Normal - the default
821 Text { text: "Hello"; font.weight: Font.DemiBold }
826 \qmlproperty bool QtQuick2::Text::font.italic
828 Sets whether the font has an italic style.
832 \qmlproperty bool QtQuick2::Text::font.underline
834 Sets whether the text is underlined.
838 \qmlproperty bool QtQuick2::Text::font.strikeout
840 Sets whether the font has a strikeout style.
844 \qmlproperty real QtQuick2::Text::font.pointSize
846 Sets the font size in points. The point size must be greater than zero.
850 \qmlproperty int QtQuick2::Text::font.pixelSize
852 Sets the font size in pixels.
854 Using this function makes the font device dependent.
855 Use \c pointSize to set the size of the font in a device independent manner.
859 \qmlproperty real QtQuick2::Text::font.letterSpacing
861 Sets the letter spacing for the font.
863 Letter spacing changes the default spacing between individual letters in the font.
864 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
868 \qmlproperty real QtQuick2::Text::font.wordSpacing
870 Sets the word spacing for the font.
872 Word spacing changes the default spacing between individual words.
873 A positive value increases the word spacing by a corresponding amount of pixels,
874 while a negative value decreases the inter-word spacing accordingly.
878 \qmlproperty enumeration QtQuick2::Text::font.capitalization
880 Sets the capitalization for the text.
883 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
884 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
885 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
886 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
887 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
891 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
894 QFont QSGText::font() const
897 return d->sourceFont;
900 void QSGText::setFont(const QFont &font)
903 if (d->sourceFont == font)
906 d->sourceFont = font;
907 QFont oldFont = d->font;
909 if (QSGDistanceFieldGlyphCache::distanceFieldEnabled())
910 d->font.setHintingPreference(QFont::PreferNoHinting);
912 if (d->font.pointSizeF() != -1) {
914 qreal size = qRound(d->font.pointSizeF()*2.0);
915 d->font.setPointSizeF(size/2.0);
918 if (oldFont != d->font)
921 emit fontChanged(d->sourceFont);
925 \qmlproperty string QtQuick2::Text::text
927 The text to display. Text supports both plain and rich text strings.
929 The item will try to automatically determine whether the text should
930 be treated as rich text. This determination is made using Qt::mightBeRichText().
932 QString QSGText::text() const
938 void QSGText::setText(const QString &n)
944 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
946 if (isComponentComplete()) {
950 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
951 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
953 d->rightToLeftText = d->text.isRightToLeft();
955 d->determineHorizontalAlignment();
958 emit textChanged(d->text);
962 \qmlproperty color QtQuick2::Text::color
966 An example of green text defined using hexadecimal notation:
974 An example of steel blue text defined using an SVG color name:
982 QColor QSGText::color() const
988 void QSGText::setColor(const QColor &color)
991 if (d->color == color)
995 d->invalidateImageCache();
996 emit colorChanged(d->color);
999 \qmlproperty enumeration QtQuick2::Text::style
1001 Set an additional text style.
1003 Supported text styles are:
1005 \o Text.Normal - the default
1013 Text { font.pointSize: 24; text: "Normal" }
1014 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1015 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1016 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1020 \image declarative-textstyle.png
1022 QSGText::TextStyle QSGText::style() const
1028 void QSGText::setStyle(QSGText::TextStyle style)
1031 if (d->style == style)
1034 // changing to/from Normal requires the boundingRect() to change
1035 if (isComponentComplete() && (d->style == Normal || style == Normal))
1038 d->invalidateImageCache();
1039 emit styleChanged(d->style);
1043 \qmlproperty color QtQuick2::Text::styleColor
1045 Defines the secondary color used by text styles.
1047 \c styleColor is used as the outline color for outlined text, and as the
1048 shadow color for raised or sunken text. If no style has been set, it is not
1052 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1057 QColor QSGText::styleColor() const
1060 return d->styleColor;
1063 void QSGText::setStyleColor(const QColor &color)
1066 if (d->styleColor == color)
1069 d->styleColor = color;
1070 d->invalidateImageCache();
1071 emit styleColorChanged(d->styleColor);
1075 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1076 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1077 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1079 Sets the horizontal and vertical alignment of the text within the Text items
1080 width and height. By default, the text is vertically aligned to the top. Horizontal
1081 alignment follows the natural alignment of the text, for example text that is read
1082 from left to right will be aligned to the left.
1084 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1085 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1086 and \c Text.AlignVCenter.
1088 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1089 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1090 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1093 When using the attached property LayoutMirroring::enabled to mirror application
1094 layouts, the horizontal alignment of text will also be mirrored. However, the property
1095 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1096 of Text, use the read-only property \c effectiveHorizontalAlignment.
1098 QSGText::HAlignment QSGText::hAlign() const
1104 void QSGText::setHAlign(HAlignment align)
1107 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1108 d->hAlignImplicit = false;
1109 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1113 void QSGText::resetHAlign()
1116 d->hAlignImplicit = true;
1117 if (d->determineHorizontalAlignment() && isComponentComplete())
1121 QSGText::HAlignment QSGText::effectiveHAlign() const
1124 QSGText::HAlignment effectiveAlignment = d->hAlign;
1125 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1126 switch (d->hAlign) {
1127 case QSGText::AlignLeft:
1128 effectiveAlignment = QSGText::AlignRight;
1130 case QSGText::AlignRight:
1131 effectiveAlignment = QSGText::AlignLeft;
1137 return effectiveAlignment;
1140 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1143 if (hAlign != alignment || forceAlign) {
1144 QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1147 emit q->horizontalAlignmentChanged(hAlign);
1148 if (oldEffectiveHAlign != q->effectiveHAlign())
1149 emit q->effectiveHorizontalAlignmentChanged();
1155 bool QSGTextPrivate::determineHorizontalAlignment()
1158 if (hAlignImplicit && q->isComponentComplete()) {
1159 bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1160 return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1165 void QSGTextPrivate::mirrorChange()
1168 if (q->isComponentComplete()) {
1169 if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1171 emit q->effectiveHorizontalAlignmentChanged();
1176 QTextDocument *QSGTextPrivate::textDocument()
1181 QSGText::VAlignment QSGText::vAlign() const
1187 void QSGText::setVAlign(VAlignment align)
1190 if (d->vAlign == align)
1194 emit verticalAlignmentChanged(align);
1198 \qmlproperty enumeration QtQuick2::Text::wrapMode
1200 Set this property to wrap the text to the Text item's width. The text will only
1201 wrap if an explicit width has been set. wrapMode can be one of:
1204 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1205 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1206 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1207 \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.
1210 QSGText::WrapMode QSGText::wrapMode() const
1216 void QSGText::setWrapMode(WrapMode mode)
1219 if (mode == d->wrapMode)
1225 emit wrapModeChanged();
1229 \qmlproperty int QtQuick2::Text::lineCount
1231 Returns the number of lines visible in the text item.
1233 This property is not supported for rich text.
1235 \sa maximumLineCount
1237 int QSGText::lineCount() const
1240 return d->lineCount;
1244 \qmlproperty bool QtQuick2::Text::truncated
1246 Returns true if the text has been truncated due to \l maximumLineCount
1249 This property is not supported for rich text.
1251 \sa maximumLineCount, elide
1253 bool QSGText::truncated() const
1256 return d->truncated;
1260 \qmlproperty int QtQuick2::Text::maximumLineCount
1262 Set this property to limit the number of lines that the text item will show.
1263 If elide is set to Text.ElideRight, the text will be elided appropriately.
1264 By default, this is the value of the largest possible integer.
1266 This property is not supported for rich text.
1268 \sa lineCount, elide
1270 int QSGText::maximumLineCount() const
1273 return d->maximumLineCount;
1276 void QSGText::setMaximumLineCount(int lines)
1280 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1281 if (d->maximumLineCount != lines) {
1282 d->maximumLineCount = lines;
1284 emit maximumLineCountChanged();
1288 void QSGText::resetMaximumLineCount()
1291 setMaximumLineCount(INT_MAX);
1292 d->elidePos = QPointF();
1293 if (d->truncated != false) {
1294 d->truncated = false;
1295 emit truncatedChanged();
1300 \qmlproperty enumeration QtQuick2::Text::textFormat
1302 The way the text property should be displayed.
1304 Supported text formats are:
1307 \o Text.AutoText (default)
1313 If the text format is \c Text.AutoText the text element
1314 will automatically determine whether the text should be treated as
1315 rich text. This determination is made using Qt::mightBeRichText().
1317 Text.StyledText is an optimized format supporting some basic text
1318 styling markup, in the style of html 3.2:
1321 <font size="4" color="#ff0000">font size and color</font>
1328 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1337 text: "<b>Hello</b> <i>World!</i>"
1341 textFormat: Text.RichText
1342 text: "<b>Hello</b> <i>World!</i>"
1346 textFormat: Text.PlainText
1347 text: "<b>Hello</b> <i>World!</i>"
1351 \o \image declarative-textformat.png
1354 QSGText::TextFormat QSGText::textFormat() const
1360 void QSGText::setTextFormat(TextFormat format)
1363 if (format == d->format)
1366 bool wasRich = d->richText;
1367 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1369 if (!wasRich && d->richText && isComponentComplete()) {
1371 d->doc->setText(d->text);
1372 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1377 emit textFormatChanged(d->format);
1381 \qmlproperty enumeration QtQuick2::Text::elide
1383 Set this property to elide parts of the text fit to the Text item's width.
1384 The text will only elide if an explicit width has been set.
1386 This property cannot be used with rich text.
1390 \o Text.ElideNone - the default
1396 If this property is set to Text.ElideRight, it can be used with multiline
1397 text. The text will only elide if maximumLineCount has been set.
1399 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1400 the first string that fits will be used, otherwise the last will be elided.
1402 Multi-length strings are ordered from longest to shortest, separated by the
1403 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1405 QSGText::TextElideMode QSGText::elideMode() const
1408 return d->elideMode;
1411 void QSGText::setElideMode(QSGText::TextElideMode mode)
1414 if (mode == d->elideMode)
1417 d->elideMode = mode;
1420 emit elideModeChanged(d->elideMode);
1424 QRectF QSGText::boundingRect() const
1428 QRect rect = d->layedOutTextRect;
1429 if (d->style != Normal)
1430 rect.adjust(-1, 0, 1, 2);
1432 // Could include font max left/right bearings to either side of rectangle.
1435 switch (d->vAlign) {
1439 rect.moveTop(h - rect.height());
1442 rect.moveTop((h - rect.height()) / 2);
1446 return QRectF(rect);
1450 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1453 if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1454 && (d->wrapMode != QSGText::NoWrap
1455 || d->elideMode != QSGText::ElideNone
1456 || d->hAlign != QSGText::AlignLeft)) {
1457 if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1458 // We need to re-elide
1461 // We just need to re-layout
1466 QSGItem::geometryChanged(newGeometry, oldGeometry);
1469 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1474 if (d->text.isEmpty()) {
1479 QRectF bounds = boundingRect();
1481 // We need to make sure the layout is done in the current thread
1482 if (d->layoutThread != QThread::currentThread())
1485 // XXX todo - some styled text can be done by the QSGTextNode
1486 if (d->richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) {
1487 bool wasDirty = d->textureImageCacheDirty;
1488 d->textureImageCacheDirty = false;
1490 if (d->imageCache.isNull()) {
1495 QSGImageNode *node = 0;
1496 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1498 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1499 d->texture = new QSGPlainTexture();
1501 d->nodeType = QSGTextPrivate::NodeIsTexture;
1503 node = static_cast<QSGImageNode *>(oldNode);
1504 Q_ASSERT(d->texture);
1508 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1509 node->setTexture(0);
1510 node->setTexture(d->texture);
1513 node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1514 node->setSourceRect(QRectF(0, 0, 1, 1));
1515 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1516 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1517 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1523 QSGTextNode *node = 0;
1524 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1526 node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1527 d->nodeType = QSGTextPrivate::NodeIsText;
1529 node = static_cast<QSGTextNode *>(oldNode);
1532 node->deleteContent();
1533 node->setMatrix(QMatrix4x4());
1538 node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1541 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1548 bool QSGText::event(QEvent *e)
1551 if (e->type() == QEvent::User) {
1552 d->checkImageCache();
1555 return QSGImplicitSizeItem::event(e);
1560 \qmlproperty real QtQuick2::Text::paintedWidth
1562 Returns the width of the text, including width past the width
1563 which is covered due to insufficient wrapping if WrapMode is set.
1565 qreal QSGText::paintedWidth() const
1568 return d->paintedSize.width();
1572 \qmlproperty real QtQuick2::Text::paintedHeight
1574 Returns the height of the text, including height past the height
1575 which is covered due to there being more text than fits in the set height.
1577 qreal QSGText::paintedHeight() const
1580 return d->paintedSize.height();
1584 \qmlproperty real QtQuick2::Text::lineHeight
1586 Sets the line height for the text.
1587 The value can be in pixels or a multiplier depending on lineHeightMode.
1589 The default value is a multiplier of 1.0.
1590 The line height must be a positive value.
1592 qreal QSGText::lineHeight() const
1595 return d->lineHeight;
1598 void QSGText::setLineHeight(qreal lineHeight)
1602 if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1605 d->lineHeight = lineHeight;
1607 emit lineHeightChanged(lineHeight);
1611 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1613 This property determines how the line height is specified.
1614 The possible values are:
1617 \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1618 line (as a multiplier). For example, set to 2 for double spacing.
1619 \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1622 QSGText::LineHeightMode QSGText::lineHeightMode() const
1625 return d->lineHeightMode;
1628 void QSGText::setLineHeightMode(LineHeightMode mode)
1631 if (mode == d->lineHeightMode)
1634 d->lineHeightMode = mode;
1637 emit lineHeightModeChanged(mode);
1641 Returns the number of resources (images) that are being loaded asynchronously.
1643 int QSGText::resourcesLoading() const
1646 return d->doc ? d->doc->resourcesLoading() : 0;
1650 void QSGText::componentComplete()
1653 QSGItem::componentComplete();
1654 if (d->updateOnComponentComplete) {
1655 d->updateOnComponentComplete = false;
1658 d->doc->setText(d->text);
1659 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1660 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1662 d->rightToLeftText = d->text.isRightToLeft();
1664 d->determineHorizontalAlignment();
1670 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1674 if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1675 event->setAccepted(false);
1676 d->activeLink.clear();
1678 d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1681 // ### may malfunction if two of the same links are clicked & dragged onto each other)
1683 if (!event->isAccepted())
1684 QSGItem::mousePressEvent(event);
1689 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1693 // ### confirm the link, and send a signal out
1694 if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1695 emit linkActivated(d->activeLink);
1697 event->setAccepted(false);
1699 if (!event->isAccepted())
1700 QSGItem::mouseReleaseEvent(event);
1705 #include "qsgtext.moc"