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), layoutThread(0), nodeType(NodeIsNull)
112 cacheAllTextAsImage = enableImageCache();
115 void QSGTextPrivate::init()
118 q->setAcceptedMouseButtons(Qt::LeftButton);
119 q->setFlag(QSGItem::ItemHasContents);
122 QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
123 : QTextDocument(parent), outstanding(0)
125 setUndoRedoEnabled(false);
128 QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
130 if (!m_resources.isEmpty())
131 qDeleteAll(m_resources);
134 QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
136 QDeclarativeContext *context = qmlContext(parent());
137 QUrl url = context->resolvedUrl(name);
139 if (type == QTextDocument::ImageResource) {
140 QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
142 if (iter == m_resources.end()) {
143 QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
144 iter = m_resources.insert(name, p);
146 if (p->isLoading()) {
147 p->connectFinished(this, SLOT(requestFinished()));
152 QDeclarativePixmap *p = *iter;
155 } else if (p->isError()) {
156 if (!errors.contains(url)) {
158 qmlInfo(parent()) << p->error();
163 return QTextDocument::loadResource(type,url); // The *resolved* URL
166 void QSGTextDocumentWithImageResources::requestFinished()
169 if (outstanding == 0) {
170 QSGText *textItem = static_cast<QSGText*>(parent());
171 QString text = textItem->text();
172 #ifndef QT_NO_TEXTHTMLPARSER
177 QSGTextPrivate *d = QSGTextPrivate::get(textItem);
182 void QSGTextDocumentWithImageResources::setText(const QString &text)
184 if (!m_resources.isEmpty()) {
185 qDeleteAll(m_resources);
190 #ifndef QT_NO_TEXTHTMLPARSER
197 QSet<QUrl> QSGTextDocumentWithImageResources::errors;
199 QSGTextPrivate::~QSGTextPrivate()
203 qreal QSGTextPrivate::getImplicitWidth() const
205 if (!requireImplicitWidth) {
206 // We don't calculate implicitWidth unless it is required.
207 // We need to force a size update now to ensure implicitWidth is calculated
208 QSGTextPrivate *me = const_cast<QSGTextPrivate*>(this);
209 me->requireImplicitWidth = true;
212 return implicitWidth;
215 void QSGTextPrivate::updateLayout()
218 if (!q->isComponentComplete()) {
219 updateOnComponentComplete = true;
223 layoutTextElided = false;
224 // Setup instance of QTextLayout for all cases other than richtext
226 layout.clearLayout();
227 layout.setFont(font);
228 if (format != QSGText::StyledText) {
230 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
231 singleline = !tmp.contains(QChar::LineSeparator);
232 if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) {
233 QFontMetrics fm(font);
234 tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
236 layoutTextElided = true;
239 emit q->truncatedChanged();
246 QDeclarativeStyledText::parse(text, layout);
250 QTextBlockFormat::LineHeightTypes type;
251 type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
252 QTextBlockFormat blockFormat;
253 blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type);
254 for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
255 QTextCursor cursor(it);
256 cursor.setBlockFormat(blockFormat);
263 void QSGTextPrivate::updateSize()
267 if (!q->isComponentComplete()) {
268 updateOnComponentComplete = true;
272 if (!requireImplicitWidth) {
273 emit q->implicitWidthChanged();
274 // if the implicitWidth is used, then updateSize() has already been called (recursively)
275 if (requireImplicitWidth)
279 invalidateImageCache();
281 QFontMetrics fm(font);
282 if (text.isEmpty()) {
283 q->setImplicitWidth(0);
284 q->setImplicitHeight(fm.height());
285 paintedSize = QSize(0, fm.height());
286 emit q->paintedSizeChanged();
291 int dy = q->height();
294 layoutThread = QThread::currentThread();
296 //setup instance of QTextLayout for all cases other than richtext
298 QRect textRect = setupTextLayout();
299 layedOutTextRect = textRect;
300 size = textRect.size();
303 singleline = false; // richtext can't elide or be optimized for single-line case
305 doc->setDefaultFont(font);
306 QSGText::HAlignment horizontalAlignment = q->effectiveHAlign();
307 if (rightToLeftText) {
308 if (horizontalAlignment == QSGText::AlignLeft)
309 horizontalAlignment = QSGText::AlignRight;
310 else if (horizontalAlignment == QSGText::AlignRight)
311 horizontalAlignment = QSGText::AlignLeft;
314 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
315 option.setWrapMode(QTextOption::WrapMode(wrapMode));
316 doc->setDefaultTextOption(option);
317 if (requireImplicitWidth && q->widthValid()) {
318 doc->setTextWidth(-1);
319 naturalWidth = doc->idealWidth();
321 if (wrapMode != QSGText::NoWrap && q->widthValid())
322 doc->setTextWidth(q->width());
324 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
325 dy -= (int)doc->size().height();
326 QSize dsize = doc->size().toSize();
327 layedOutTextRect = QRect(QPoint(0,0), dsize);
328 size = QSize(int(doc->idealWidth()),dsize.height());
332 if (q->heightValid()) {
333 if (vAlign == QSGText::AlignBottom)
335 else if (vAlign == QSGText::AlignVCenter)
338 q->setBaselineOffset(fm.ascent() + yoff);
340 //### need to comfirm cost of always setting these for richText
341 internalWidthUpdate = true;
342 if (!q->widthValid())
343 q->setImplicitWidth(size.width());
344 else if (requireImplicitWidth)
345 q->setImplicitWidth(naturalWidth);
346 internalWidthUpdate = false;
348 q->setImplicitHeight(size.height());
349 if (paintedSize != size) {
351 emit q->paintedSizeChanged();
357 Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
359 Returns the size of the final text. This can be used to position the text vertically (the text is
360 already absolutely positioned horizontally).
362 QRect QSGTextPrivate::setupTextLayout()
364 // ### text layout handling should be profiled and optimized as needed
365 // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
367 layout.setCacheEnabled(true);
370 int visibleCount = 0;
374 lineWidth = q->width();
376 QTextOption textOption = layout.textOption();
377 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
378 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
379 layout.setTextOption(textOption);
381 bool elideText = false;
382 bool truncate = false;
384 QFontMetrics fm(layout.font());
385 elidePos = QPointF();
387 if (requireImplicitWidth && q->widthValid()) {
388 // requires an extra layout
390 if (layoutTextElided) {
391 // We have provided elided text to the layout, but we must calculate unelided width.
392 elidedText = layout.text();
393 layout.setText(text);
395 layout.beginLayout();
397 QTextLine line = layout.createLine();
403 for (int i = 0; i < layout.lineCount(); ++i) {
404 QTextLine line = layout.lineAt(i);
405 br = br.united(line.naturalTextRect());
407 naturalWidth = br.width();
408 if (layoutTextElided)
409 layout.setText(elidedText);
412 if (maximumLineCountValid) {
413 layout.beginLayout();
416 int linesLeft = maximumLineCount;
417 int visibleTextLength = 0;
418 while (linesLeft > 0) {
419 QTextLine line = layout.createLine();
425 line.setLineWidth(lineWidth);
426 visibleTextLength += line.textLength();
428 if (--linesLeft == 0) {
429 if (visibleTextLength < text.length()) {
431 if (elideMode==QSGText::ElideRight && q->widthValid()) {
432 qreal elideWidth = fm.width(elideChar);
433 // Need to correct for alignment
434 line.setLineWidth(lineWidth-elideWidth);
435 if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
436 line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
437 elidePos.setX(line.naturalTextRect().left() - elideWidth);
439 elidePos.setX(line.naturalTextRect().right());
449 if (truncated != truncate) {
450 truncated = truncate;
451 emit q->truncatedChanged();
454 layout.beginLayout();
456 QTextLine line = layout.createLine();
461 line.setLineWidth(lineWidth);
468 for (int i = 0; i < layout.lineCount(); ++i) {
469 QTextLine line = layout.lineAt(i);
471 line.setPosition(QPointF(line.position().x(), height));
472 if (elideText && i == layout.lineCount()-1) {
473 elidePos.setY(height + fm.ascent());
474 br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
476 br = br.united(line.naturalTextRect());
477 height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
479 br.setHeight(height);
481 if (!q->widthValid())
482 naturalWidth = br.width();
484 //Update the number of visible lines
485 if (lineCount != visibleCount) {
486 lineCount = visibleCount;
487 emit q->lineCountChanged();
490 return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
494 Returns a painted version of the QSGTextPrivate::layout QTextLayout.
495 If \a drawStyle is true, the style color overrides all colors in the document.
497 QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
499 QSize size = layedOutTextRect.size();
503 if (!size.isEmpty()) {
504 img.fill(Qt::transparent);
506 bool oldSmooth = qt_applefontsmoothing_enabled;
507 qt_applefontsmoothing_enabled = false;
511 qt_applefontsmoothing_enabled = oldSmooth;
513 drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
519 Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos. If
520 \a drawStyle is true, the style color overrides all colors in the document.
522 void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
525 painter->setPen(styleColor);
527 painter->setPen(color);
528 painter->setFont(font);
529 layout.draw(painter, pos);
530 if (!elidePos.isNull())
531 painter->drawText(pos + elidePos, elideChar);
535 Returns a painted version of the QSGTextPrivate::doc QTextDocument.
536 If \a drawStyle is true, the style color overrides all colors in the document.
538 QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
540 QSize size = doc->size().toSize();
544 img.fill(Qt::transparent);
546 bool oldSmooth = qt_applefontsmoothing_enabled;
547 qt_applefontsmoothing_enabled = false;
551 qt_applefontsmoothing_enabled = oldSmooth;
554 QAbstractTextDocumentLayout::PaintContext context;
556 QTextOption oldOption(doc->defaultTextOption());
558 context.palette.setColor(QPalette::Text, styleColor);
559 QTextOption colorOption(doc->defaultTextOption());
560 colorOption.setFlags(QTextOption::SuppressColors);
561 doc->setDefaultTextOption(colorOption);
563 context.palette.setColor(QPalette::Text, color);
565 doc->documentLayout()->draw(&p, context);
567 doc->setDefaultTextOption(oldOption);
572 Mark the image cache as dirty.
574 void QSGTextPrivate::invalidateImageCache()
578 if(richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QSGText::Normal)){//If actually using the image cache
582 imageCacheDirty = true;
584 if (q->isComponentComplete())
585 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
586 } else if (q->isComponentComplete())
591 Tests if the image cache is dirty, and repaints it if it is.
593 void QSGTextPrivate::checkImageCache()
597 if (!imageCacheDirty)
600 if (text.isEmpty()) {
602 imageCache = QPixmap();
610 textImage = textDocumentImage(false);
611 if (style != QSGText::Normal)
612 styledImage = textDocumentImage(true); //### should use styleColor
614 textImage = textLayoutImage(false);
615 if (style != QSGText::Normal)
616 styledImage = textLayoutImage(true); //### should use styleColor
620 case QSGText::Outline:
621 imageCache = drawOutline(textImage, styledImage);
623 case QSGText::Sunken:
624 imageCache = drawOutline(textImage, styledImage, -1);
626 case QSGText::Raised:
627 imageCache = drawOutline(textImage, styledImage, 1);
630 imageCache = textImage;
636 imageCacheDirty = false;
637 textureImageCacheDirty = true;
642 Ensures the QSGTextPrivate::doc variable is set to a valid text document
644 void QSGTextPrivate::ensureDoc()
648 doc = new QSGTextDocumentWithImageResources(q);
649 doc->setDocumentMargin(0);
654 Draw \a styleSource as an outline around \a source and return the new image.
656 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
658 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
659 img.fill(Qt::transparent);
664 pos += QPoint(-1, 0);
665 ppm.drawPixmap(pos, styleSource);
667 ppm.drawPixmap(pos, styleSource);
668 pos += QPoint(-1, -1);
669 ppm.drawPixmap(pos, styleSource);
671 ppm.drawPixmap(pos, styleSource);
673 pos += QPoint(0, -1);
674 ppm.drawPixmap(pos, source);
681 Draw \a styleSource below \a source at \a yOffset and return the new image.
683 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
685 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
686 img.fill(Qt::transparent);
690 ppm.drawPixmap(QPoint(0, yOffset), styleSource);
691 ppm.drawPixmap(0, 0, source);
699 \qmlclass Text QSGText
700 \inqmlmodule QtQuick 2
701 \ingroup qml-basic-visual-elements
702 \brief The Text item allows you to add formatted text to a scene.
705 Text items can display both plain and rich text. For example, red text with
706 a specific font and size can be defined like this:
711 font.family: "Helvetica"
717 Rich text is defined using HTML-style markup:
721 text: "<b>Hello</b> <i>World!</i>"
725 \image declarative-text.png
727 If height and width are not explicitly set, Text will attempt to determine how
728 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
729 prefer width to height (all text will be placed on a single line).
731 The \l elide property can alternatively be used to fit a single line of
732 plain text to a set width.
734 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
735 HTML img tags that load remote images, the text is reloaded.
737 Text provides read-only text. For editable text, see \l TextEdit.
739 \sa {declarative/text/fonts}{Fonts example}
741 QSGText::QSGText(QSGItem *parent)
742 : QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
753 \qmlproperty bool QtQuick2::Text::clip
754 This property holds whether the text is clipped.
756 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
758 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
762 \qmlproperty bool QtQuick2::Text::smooth
764 This property holds whether the text is smoothly scaled or transformed.
766 Smooth filtering gives better visual quality, but is slower. If
767 the item is displayed at its natural size, this property has no visual or
770 \note Generally scaling artifacts are only visible if the item is stationary on
771 the screen. A common pattern when animating an item is to disable smooth
772 filtering at the beginning of the animation and reenable it at the conclusion.
776 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
778 This handler is called when the user clicks on a link embedded in the text.
779 The link must be in rich text or HTML format and the
780 \a link string provides access to the particular link.
782 \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
784 The example code will display the text
785 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
787 Clicking on the highlighted link will output
788 \tt{http://qt.nokia.com link activated} to the console.
792 \qmlproperty string QtQuick2::Text::font.family
794 Sets the family name of the font.
796 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
797 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
798 If the family isn't available a family will be set using the font matching algorithm.
802 \qmlproperty bool QtQuick2::Text::font.bold
804 Sets whether the font weight is bold.
808 \qmlproperty enumeration QtQuick2::Text::font.weight
810 Sets the font's weight.
812 The weight can be one of:
815 \o Font.Normal - the default
822 Text { text: "Hello"; font.weight: Font.DemiBold }
827 \qmlproperty bool QtQuick2::Text::font.italic
829 Sets whether the font has an italic style.
833 \qmlproperty bool QtQuick2::Text::font.underline
835 Sets whether the text is underlined.
839 \qmlproperty bool QtQuick2::Text::font.strikeout
841 Sets whether the font has a strikeout style.
845 \qmlproperty real QtQuick2::Text::font.pointSize
847 Sets the font size in points. The point size must be greater than zero.
851 \qmlproperty int QtQuick2::Text::font.pixelSize
853 Sets the font size in pixels.
855 Using this function makes the font device dependent.
856 Use \c pointSize to set the size of the font in a device independent manner.
860 \qmlproperty real QtQuick2::Text::font.letterSpacing
862 Sets the letter spacing for the font.
864 Letter spacing changes the default spacing between individual letters in the font.
865 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
869 \qmlproperty real QtQuick2::Text::font.wordSpacing
871 Sets the word spacing for the font.
873 Word spacing changes the default spacing between individual words.
874 A positive value increases the word spacing by a corresponding amount of pixels,
875 while a negative value decreases the inter-word spacing accordingly.
879 \qmlproperty enumeration QtQuick2::Text::font.capitalization
881 Sets the capitalization for the text.
884 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
885 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
886 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
887 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
888 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
892 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
895 QFont QSGText::font() const
898 return d->sourceFont;
901 void QSGText::setFont(const QFont &font)
904 if (d->sourceFont == font)
907 d->sourceFont = font;
908 QFont oldFont = d->font;
910 if (!qmlDisableDistanceField())
911 d->font.setHintingPreference(QFont::PreferNoHinting);
913 if (d->font.pointSizeF() != -1) {
915 qreal size = qRound(d->font.pointSizeF()*2.0);
916 d->font.setPointSizeF(size/2.0);
919 if (oldFont != d->font)
922 emit fontChanged(d->sourceFont);
926 \qmlproperty string QtQuick2::Text::text
928 The text to display. Text supports both plain and rich text strings.
930 The item will try to automatically determine whether the text should
931 be treated as rich text. This determination is made using Qt::mightBeRichText().
933 QString QSGText::text() const
939 void QSGText::setText(const QString &n)
945 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
947 if (isComponentComplete()) {
951 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
952 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
954 d->rightToLeftText = d->text.isRightToLeft();
956 d->determineHorizontalAlignment();
959 emit textChanged(d->text);
963 \qmlproperty color QtQuick2::Text::color
967 An example of green text defined using hexadecimal notation:
975 An example of steel blue text defined using an SVG color name:
983 QColor QSGText::color() const
989 void QSGText::setColor(const QColor &color)
992 if (d->color == color)
996 d->invalidateImageCache();
997 emit colorChanged(d->color);
1000 \qmlproperty enumeration QtQuick2::Text::style
1002 Set an additional text style.
1004 Supported text styles are:
1006 \o Text.Normal - the default
1014 Text { font.pointSize: 24; text: "Normal" }
1015 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1016 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1017 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1021 \image declarative-textstyle.png
1023 QSGText::TextStyle QSGText::style() const
1029 void QSGText::setStyle(QSGText::TextStyle style)
1032 if (d->style == style)
1035 // changing to/from Normal requires the boundingRect() to change
1036 if (isComponentComplete() && (d->style == Normal || style == Normal))
1039 d->invalidateImageCache();
1040 emit styleChanged(d->style);
1044 \qmlproperty color QtQuick2::Text::styleColor
1046 Defines the secondary color used by text styles.
1048 \c styleColor is used as the outline color for outlined text, and as the
1049 shadow color for raised or sunken text. If no style has been set, it is not
1053 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1058 QColor QSGText::styleColor() const
1061 return d->styleColor;
1064 void QSGText::setStyleColor(const QColor &color)
1067 if (d->styleColor == color)
1070 d->styleColor = color;
1071 d->invalidateImageCache();
1072 emit styleColorChanged(d->styleColor);
1076 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1077 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1078 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1080 Sets the horizontal and vertical alignment of the text within the Text items
1081 width and height. By default, the text is vertically aligned to the top. Horizontal
1082 alignment follows the natural alignment of the text, for example text that is read
1083 from left to right will be aligned to the left.
1085 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1086 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1087 and \c Text.AlignVCenter.
1089 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1090 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1091 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1094 When using the attached property LayoutMirroring::enabled to mirror application
1095 layouts, the horizontal alignment of text will also be mirrored. However, the property
1096 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1097 of Text, use the read-only property \c effectiveHorizontalAlignment.
1099 QSGText::HAlignment QSGText::hAlign() const
1105 void QSGText::setHAlign(HAlignment align)
1108 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1109 d->hAlignImplicit = false;
1110 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1114 void QSGText::resetHAlign()
1117 d->hAlignImplicit = true;
1118 if (d->determineHorizontalAlignment() && isComponentComplete())
1122 QSGText::HAlignment QSGText::effectiveHAlign() const
1125 QSGText::HAlignment effectiveAlignment = d->hAlign;
1126 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1127 switch (d->hAlign) {
1128 case QSGText::AlignLeft:
1129 effectiveAlignment = QSGText::AlignRight;
1131 case QSGText::AlignRight:
1132 effectiveAlignment = QSGText::AlignLeft;
1138 return effectiveAlignment;
1141 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1144 if (hAlign != alignment || forceAlign) {
1145 QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1148 emit q->horizontalAlignmentChanged(hAlign);
1149 if (oldEffectiveHAlign != q->effectiveHAlign())
1150 emit q->effectiveHorizontalAlignmentChanged();
1156 bool QSGTextPrivate::determineHorizontalAlignment()
1159 if (hAlignImplicit && q->isComponentComplete()) {
1160 bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1161 return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1166 void QSGTextPrivate::mirrorChange()
1169 if (q->isComponentComplete()) {
1170 if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1172 emit q->effectiveHorizontalAlignmentChanged();
1177 QTextDocument *QSGTextPrivate::textDocument()
1182 QSGText::VAlignment QSGText::vAlign() const
1188 void QSGText::setVAlign(VAlignment align)
1191 if (d->vAlign == align)
1195 emit verticalAlignmentChanged(align);
1199 \qmlproperty enumeration QtQuick2::Text::wrapMode
1201 Set this property to wrap the text to the Text item's width. The text will only
1202 wrap if an explicit width has been set. wrapMode can be one of:
1205 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1206 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1207 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1208 \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.
1211 QSGText::WrapMode QSGText::wrapMode() const
1217 void QSGText::setWrapMode(WrapMode mode)
1220 if (mode == d->wrapMode)
1226 emit wrapModeChanged();
1230 \qmlproperty int QtQuick2::Text::lineCount
1232 Returns the number of lines visible in the text item.
1234 This property is not supported for rich text.
1236 \sa maximumLineCount
1238 int QSGText::lineCount() const
1241 return d->lineCount;
1245 \qmlproperty bool QtQuick2::Text::truncated
1247 Returns true if the text has been truncated due to \l maximumLineCount
1250 This property is not supported for rich text.
1252 \sa maximumLineCount, elide
1254 bool QSGText::truncated() const
1257 return d->truncated;
1261 \qmlproperty int QtQuick2::Text::maximumLineCount
1263 Set this property to limit the number of lines that the text item will show.
1264 If elide is set to Text.ElideRight, the text will be elided appropriately.
1265 By default, this is the value of the largest possible integer.
1267 This property is not supported for rich text.
1269 \sa lineCount, elide
1271 int QSGText::maximumLineCount() const
1274 return d->maximumLineCount;
1277 void QSGText::setMaximumLineCount(int lines)
1281 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1282 if (d->maximumLineCount != lines) {
1283 d->maximumLineCount = lines;
1285 emit maximumLineCountChanged();
1289 void QSGText::resetMaximumLineCount()
1292 setMaximumLineCount(INT_MAX);
1293 d->elidePos = QPointF();
1294 if (d->truncated != false) {
1295 d->truncated = false;
1296 emit truncatedChanged();
1301 \qmlproperty enumeration QtQuick2::Text::textFormat
1303 The way the text property should be displayed.
1305 Supported text formats are:
1308 \o Text.AutoText (default)
1314 If the text format is \c Text.AutoText the text element
1315 will automatically determine whether the text should be treated as
1316 rich text. This determination is made using Qt::mightBeRichText().
1318 Text.StyledText is an optimized format supporting some basic text
1319 styling markup, in the style of html 3.2:
1322 <font size="4" color="#ff0000">font size and color</font>
1329 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1338 text: "<b>Hello</b> <i>World!</i>"
1342 textFormat: Text.RichText
1343 text: "<b>Hello</b> <i>World!</i>"
1347 textFormat: Text.PlainText
1348 text: "<b>Hello</b> <i>World!</i>"
1352 \o \image declarative-textformat.png
1355 QSGText::TextFormat QSGText::textFormat() const
1361 void QSGText::setTextFormat(TextFormat format)
1364 if (format == d->format)
1367 bool wasRich = d->richText;
1368 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1370 if (!wasRich && d->richText && isComponentComplete()) {
1372 d->doc->setText(d->text);
1373 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1378 emit textFormatChanged(d->format);
1382 \qmlproperty enumeration QtQuick2::Text::elide
1384 Set this property to elide parts of the text fit to the Text item's width.
1385 The text will only elide if an explicit width has been set.
1387 This property cannot be used with rich text.
1391 \o Text.ElideNone - the default
1397 If this property is set to Text.ElideRight, it can be used with multiline
1398 text. The text will only elide if maximumLineCount has been set.
1400 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1401 the first string that fits will be used, otherwise the last will be elided.
1403 Multi-length strings are ordered from longest to shortest, separated by the
1404 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1406 QSGText::TextElideMode QSGText::elideMode() const
1409 return d->elideMode;
1412 void QSGText::setElideMode(QSGText::TextElideMode mode)
1415 if (mode == d->elideMode)
1418 d->elideMode = mode;
1421 emit elideModeChanged(d->elideMode);
1425 QRectF QSGText::boundingRect() const
1429 QRect rect = d->layedOutTextRect;
1430 if (d->style != Normal)
1431 rect.adjust(-1, 0, 1, 2);
1433 // Could include font max left/right bearings to either side of rectangle.
1436 switch (d->vAlign) {
1440 rect.moveTop(h - rect.height());
1443 rect.moveTop((h - rect.height()) / 2);
1447 return QRectF(rect);
1451 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1454 if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1455 && (d->wrapMode != QSGText::NoWrap
1456 || d->elideMode != QSGText::ElideNone
1457 || d->hAlign != QSGText::AlignLeft)) {
1458 if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1459 // We need to re-elide
1462 // We just need to re-layout
1467 QSGItem::geometryChanged(newGeometry, oldGeometry);
1470 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1475 if (d->text.isEmpty()) {
1480 QRectF bounds = boundingRect();
1482 // We need to make sure the layout is done in the current thread
1483 if (d->layoutThread != QThread::currentThread())
1486 // XXX todo - some styled text can be done by the QSGTextNode
1487 if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1488 bool wasDirty = d->textureImageCacheDirty;
1489 d->textureImageCacheDirty = false;
1491 if (d->imageCache.isNull()) {
1496 QSGImageNode *node = 0;
1497 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1499 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1500 d->texture = new QSGPlainTexture();
1502 d->nodeType = QSGTextPrivate::NodeIsTexture;
1504 node = static_cast<QSGImageNode *>(oldNode);
1505 Q_ASSERT(d->texture);
1509 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1510 node->setTexture(0);
1511 node->setTexture(d->texture);
1514 node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1515 node->setSourceRect(QRectF(0, 0, 1, 1));
1516 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1517 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1518 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1524 QSGTextNode *node = 0;
1525 if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1527 node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1528 d->nodeType = QSGTextPrivate::NodeIsText;
1530 node = static_cast<QSGTextNode *>(oldNode);
1533 node->deleteContent();
1534 node->setMatrix(QMatrix4x4());
1539 node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1542 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1549 bool QSGText::event(QEvent *e)
1552 if (e->type() == QEvent::User) {
1553 d->checkImageCache();
1556 return QSGImplicitSizeItem::event(e);
1561 \qmlproperty real QtQuick2::Text::paintedWidth
1563 Returns the width of the text, including width past the width
1564 which is covered due to insufficient wrapping if WrapMode is set.
1566 qreal QSGText::paintedWidth() const
1569 return d->paintedSize.width();
1573 \qmlproperty real QtQuick2::Text::paintedHeight
1575 Returns the height of the text, including height past the height
1576 which is covered due to there being more text than fits in the set height.
1578 qreal QSGText::paintedHeight() const
1581 return d->paintedSize.height();
1585 \qmlproperty real QtQuick2::Text::lineHeight
1587 Sets the line height for the text.
1588 The value can be in pixels or a multiplier depending on lineHeightMode.
1590 The default value is a multiplier of 1.0.
1591 The line height must be a positive value.
1593 qreal QSGText::lineHeight() const
1596 return d->lineHeight;
1599 void QSGText::setLineHeight(qreal lineHeight)
1603 if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1606 d->lineHeight = lineHeight;
1608 emit lineHeightChanged(lineHeight);
1612 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1614 This property determines how the line height is specified.
1615 The possible values are:
1618 \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1619 line (as a multiplier). For example, set to 2 for double spacing.
1620 \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1623 QSGText::LineHeightMode QSGText::lineHeightMode() const
1626 return d->lineHeightMode;
1629 void QSGText::setLineHeightMode(LineHeightMode mode)
1632 if (mode == d->lineHeightMode)
1635 d->lineHeightMode = mode;
1638 emit lineHeightModeChanged(mode);
1642 Returns the number of resources (images) that are being loaded asynchronously.
1644 int QSGText::resourcesLoading() const
1647 return d->doc ? d->doc->resourcesLoading() : 0;
1651 void QSGText::componentComplete()
1654 QSGItem::componentComplete();
1655 if (d->updateOnComponentComplete) {
1656 d->updateOnComponentComplete = false;
1659 d->doc->setText(d->text);
1660 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1661 d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1663 d->rightToLeftText = d->text.isRightToLeft();
1665 d->determineHorizontalAlignment();
1671 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1675 if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1676 event->setAccepted(false);
1677 d->activeLink.clear();
1679 d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1682 // ### may malfunction if two of the same links are clicked & dragged onto each other)
1684 if (!event->isAccepted())
1685 QSGItem::mousePressEvent(event);
1690 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1694 // ### confirm the link, and send a signal out
1695 if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1696 emit linkActivated(d->activeLink);
1698 event->setAccepted(false);
1700 if (!event->isAccepted())
1701 QSGItem::mouseReleaseEvent(event);
1706 #include "qsgtext.moc"