Improvements to textFormat: Text.StyledText
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgtext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgtext_p.h"
43 #include "qsgtext_p_p.h"
44
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>
51
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>
60
61 #include <private/qdeclarativestyledtext_p.h>
62 #include <private/qdeclarativepixmapcache_p.h>
63
64 #include <qmath.h>
65 #include <limits.h>
66
67 QT_BEGIN_NAMESPACE
68
69 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
70
71 class QSGTextDocumentWithImageResources : public QTextDocument {
72     Q_OBJECT
73
74 public:
75     QSGTextDocumentWithImageResources(QSGText *parent);
76     virtual ~QSGTextDocumentWithImageResources();
77
78     void setText(const QString &);
79     int resourcesLoading() const { return outstanding; }
80
81 protected:
82     QVariant loadResource(int type, const QUrl &name);
83
84 private slots:
85     void requestFinished();
86
87 private:
88     QHash<QUrl, QDeclarativePixmap *> m_resources;
89
90     int outstanding;
91     static QSet<QUrl> errors;
92 };
93
94 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
95 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
96
97 QString QSGTextPrivate::elideChar = QString(0x2026);
98
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),
105   texture(0),
106   imageCacheDirty(false), updateOnComponentComplete(true),
107   richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
108   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
109   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
110   doc(0), nodeType(NodeIsNull)
111
112 #if defined(Q_OS_MAC)
113   , layoutThread(0)
114 #endif
115
116 {
117     cacheAllTextAsImage = enableImageCache();
118 }
119
120 void QSGTextPrivate::init()
121 {
122     Q_Q(QSGText);
123     q->setAcceptedMouseButtons(Qt::LeftButton);
124     q->setFlag(QSGItem::ItemHasContents);
125 }
126
127 QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
128 : QTextDocument(parent), outstanding(0)
129 {
130     setUndoRedoEnabled(false);
131 }
132
133 QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
134 {
135     if (!m_resources.isEmpty())
136         qDeleteAll(m_resources);
137 }
138
139 QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
140 {
141     QDeclarativeContext *context = qmlContext(parent());
142     QUrl url = context->resolvedUrl(name);
143
144     if (type == QTextDocument::ImageResource) {
145         QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
146
147         if (iter == m_resources.end()) {
148             QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
149             iter = m_resources.insert(name, p);
150
151             if (p->isLoading()) {
152                 p->connectFinished(this, SLOT(requestFinished()));
153                 outstanding++;
154             }
155         }
156
157         QDeclarativePixmap *p = *iter;
158         if (p->isReady()) {
159             return p->pixmap();
160         } else if (p->isError()) {
161             if (!errors.contains(url)) {
162                 errors.insert(url);
163                 qmlInfo(parent()) << p->error();
164             }
165         }
166     }
167
168     return QTextDocument::loadResource(type,url); // The *resolved* URL
169 }
170
171 void QSGTextDocumentWithImageResources::requestFinished()
172 {
173     outstanding--;
174     if (outstanding == 0) {
175         QSGText *textItem = static_cast<QSGText*>(parent());
176         QString text = textItem->text();
177 #ifndef QT_NO_TEXTHTMLPARSER
178         setHtml(text);
179 #else
180         setPlainText(text);
181 #endif
182         QSGTextPrivate *d = QSGTextPrivate::get(textItem);
183         d->updateLayout();
184     }
185 }
186
187 void QSGTextDocumentWithImageResources::setText(const QString &text)
188 {
189     if (!m_resources.isEmpty()) {
190         qDeleteAll(m_resources);
191         m_resources.clear();
192         outstanding = 0;
193     }
194
195 #ifndef QT_NO_TEXTHTMLPARSER
196     setHtml(text);
197 #else
198     setPlainText(text);
199 #endif
200 }
201
202 QSet<QUrl> QSGTextDocumentWithImageResources::errors;
203
204 QSGTextPrivate::~QSGTextPrivate()
205 {
206 }
207
208 qreal QSGTextPrivate::getImplicitWidth() const
209 {
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;
215         me->updateSize();
216     }
217     return implicitWidth;
218 }
219
220 void QSGTextPrivate::updateLayout()
221 {
222     Q_Q(QSGText);
223     if (!q->isComponentComplete()) {
224         updateOnComponentComplete = true;
225         return;
226     }
227
228     layoutTextElided = false;
229     // Setup instance of QTextLayout for all cases other than richtext
230     if (!richText) {
231         layout.clearLayout();
232         layout.setFont(font);
233         if (format != QSGText::StyledText) {
234             QString tmp = text;
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());
240                 if (tmp != text) {
241                     layoutTextElided = true;
242                     if (!truncated) {
243                         truncated = true;
244                         emit q->truncatedChanged();
245                     }
246                 }
247             }
248             layout.setText(tmp);
249         } else {
250             singleline = false;
251             QDeclarativeStyledText::parse(text, layout);
252         }
253     } else {
254         ensureDoc();
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);
262         }
263     }
264
265     updateSize();
266 }
267
268 void QSGTextPrivate::updateSize()
269 {
270     Q_Q(QSGText);
271
272     if (!q->isComponentComplete()) {
273         updateOnComponentComplete = true;
274         return;
275     }
276
277     if (!requireImplicitWidth) {
278         emit q->implicitWidthChanged();
279         // if the implicitWidth is used, then updateSize() has already been called (recursively)
280         if (requireImplicitWidth)
281             return;
282     }
283
284     invalidateImageCache();
285
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();
292         q->update();
293         return;
294     }
295
296     int dy = q->height();
297     QSize size(0, 0);
298
299 #if defined(Q_OS_MAC)
300     layoutThread = QThread::currentThread();
301 #endif
302
303     //setup instance of QTextLayout for all cases other than richtext
304     if (!richText) {
305         QRect textRect = setupTextLayout();
306         layedOutTextRect = textRect;
307         size = textRect.size();
308         dy -= size.height();
309     } else {
310         singleline = false; // richtext can't elide or be optimized for single-line case
311         ensureDoc();
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;
319         }
320         QTextOption option;
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();
329         }
330         if (wrapMode != QSGText::NoWrap && q->widthValid())
331             doc->setTextWidth(q->width());
332         else
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());
338     }
339     int yoff = 0;
340
341     if (q->heightValid()) {
342         if (vAlign == QSGText::AlignBottom)
343             yoff = dy;
344         else if (vAlign == QSGText::AlignVCenter)
345             yoff = dy/2;
346     }
347     q->setBaselineOffset(fm.ascent() + yoff);
348
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;
356
357     q->setImplicitHeight(size.height());
358     if (paintedSize != size) {
359         paintedSize = size;
360         emit q->paintedSizeChanged();
361     }
362     q->update();
363 }
364
365 /*!
366     Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
367
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).
370 */
371 QRect QSGTextPrivate::setupTextLayout()
372 {
373     // ### text layout handling should be profiled and optimized as needed
374     // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
375     Q_Q(QSGText);
376     layout.setCacheEnabled(true);
377
378     qreal lineWidth = 0;
379     int visibleCount = 0;
380
381     //set manual width
382     if (q->widthValid())
383         lineWidth = q->width();
384
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);
391
392     bool elideText = false;
393     bool truncate = false;
394
395     QFontMetrics fm(layout.font());
396     elidePos = QPointF();
397
398     if (requireImplicitWidth && q->widthValid()) {
399         // requires an extra layout
400         QString elidedText;
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);
405         }
406         layout.beginLayout();
407         forever {
408             QTextLine line = layout.createLine();
409             if (!line.isValid())
410                 break;
411         }
412         layout.endLayout();
413         QRectF br;
414         for (int i = 0; i < layout.lineCount(); ++i) {
415             QTextLine line = layout.lineAt(i);
416             br = br.united(line.naturalTextRect());
417         }
418         naturalWidth = br.width();
419         if (layoutTextElided)
420             layout.setText(elidedText);
421     }
422
423     if (maximumLineCountValid) {
424         layout.beginLayout();
425         if (!lineWidth)
426             lineWidth = INT_MAX;
427         int linesLeft = maximumLineCount;
428         int visibleTextLength = 0;
429         while (linesLeft > 0) {
430             QTextLine line = layout.createLine();
431             if (!line.isValid())
432                 break;
433
434             visibleCount++;
435             if (lineWidth)
436                 line.setLineWidth(lineWidth);
437             visibleTextLength += line.textLength();
438
439             if (--linesLeft == 0) {
440                 if (visibleTextLength < text.length()) {
441                     truncate = true;
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);
449                         } else {
450                             elidePos.setX(line.naturalTextRect().right());
451                         }
452                         elideText = true;
453                     }
454                 }
455             }
456         }
457         layout.endLayout();
458
459         //Update truncated
460         if (truncated != truncate) {
461             truncated = truncate;
462             emit q->truncatedChanged();
463         }
464     } else {
465         layout.beginLayout();
466         forever {
467             QTextLine line = layout.createLine();
468             if (!line.isValid())
469                 break;
470             visibleCount++;
471             if (lineWidth)
472                 line.setLineWidth(lineWidth);
473         }
474         layout.endLayout();
475     }
476
477     qreal height = 0;
478     QRectF br;
479     for (int i = 0; i < layout.lineCount(); ++i) {
480         QTextLine line = layout.lineAt(i);
481         // set line spacing
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())));
486         }
487         br = br.united(line.naturalTextRect());
488         height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
489     }
490     br.setHeight(height);
491
492     if (!q->widthValid())
493         naturalWidth = br.width();
494
495     //Update the number of visible lines
496     if (lineCount != visibleCount) {
497         lineCount = visibleCount;
498         emit q->lineCountChanged();
499     }
500
501     return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
502 }
503
504 /*!
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.
507 */
508 QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
509 {
510     QSize size = layedOutTextRect.size();
511
512     //paint text
513     QPixmap img(size);
514     if (!size.isEmpty()) {
515         img.fill(Qt::transparent);
516 #ifdef Q_WS_MAC
517         bool oldSmooth = qt_applefontsmoothing_enabled;
518         qt_applefontsmoothing_enabled = false;
519 #endif
520         QPainter p(&img);
521 #ifdef Q_WS_MAC
522         qt_applefontsmoothing_enabled = oldSmooth;
523 #endif
524         drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
525     }
526     return img;
527 }
528
529 /*!
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.
532 */
533 void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
534 {
535     if (drawStyle)
536         painter->setPen(styleColor);
537     else
538         painter->setPen(color);
539     painter->setFont(font);
540     layout.draw(painter, pos);
541     if (!elidePos.isNull())
542         painter->drawText(pos + elidePos, elideChar);
543 }
544
545 /*!
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.
548 */
549 QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
550 {
551     QSize size = doc->size().toSize();
552
553     //paint text
554     QPixmap img(size);
555     img.fill(Qt::transparent);
556 #ifdef Q_WS_MAC
557     bool oldSmooth = qt_applefontsmoothing_enabled;
558     qt_applefontsmoothing_enabled = false;
559 #endif
560     QPainter p(&img);
561 #ifdef Q_WS_MAC
562     qt_applefontsmoothing_enabled = oldSmooth;
563 #endif
564
565     QAbstractTextDocumentLayout::PaintContext context;
566
567     QTextOption oldOption(doc->defaultTextOption());
568     if (drawStyle) {
569         context.palette.setColor(QPalette::Text, styleColor);
570         QTextOption colorOption(doc->defaultTextOption());
571         colorOption.setFlags(QTextOption::SuppressColors);
572         doc->setDefaultTextOption(colorOption);
573     } else {
574         context.palette.setColor(QPalette::Text, color);
575     }
576     doc->documentLayout()->draw(&p, context);
577     if (drawStyle)
578         doc->setDefaultTextOption(oldOption);
579     return img;
580 }
581
582 /*!
583     Mark the image cache as dirty.
584 */
585 void QSGTextPrivate::invalidateImageCache()
586 {
587     Q_Q(QSGText);
588
589     if(richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QSGText::Normal)){//If actually using the image cache
590         if (imageCacheDirty)
591             return;
592
593         imageCacheDirty = true;
594
595         if (q->isComponentComplete())
596             QCoreApplication::postEvent(q, new QEvent(QEvent::User));
597     } else if (q->isComponentComplete())
598         q->update();
599 }
600
601 /*!
602     Tests if the image cache is dirty, and repaints it if it is.
603 */
604 void QSGTextPrivate::checkImageCache()
605 {
606     Q_Q(QSGText);
607
608     if (!imageCacheDirty)
609         return;
610
611     if (text.isEmpty()) {
612
613         imageCache = QPixmap();
614
615     } else {
616
617         QPixmap textImage;
618         QPixmap styledImage;
619
620         if (richText) {
621             textImage = textDocumentImage(false);
622             if (style != QSGText::Normal)
623                 styledImage = textDocumentImage(true); //### should use styleColor
624         } else {
625             textImage = textLayoutImage(false);
626             if (style != QSGText::Normal)
627                 styledImage = textLayoutImage(true); //### should use styleColor
628         }
629
630         switch (style) {
631         case QSGText::Outline:
632             imageCache = drawOutline(textImage, styledImage);
633             break;
634         case QSGText::Sunken:
635             imageCache = drawOutline(textImage, styledImage, -1);
636             break;
637         case QSGText::Raised:
638             imageCache = drawOutline(textImage, styledImage, 1);
639             break;
640         default:
641             imageCache = textImage;
642             break;
643         }
644
645     }
646
647     imageCacheDirty = false;
648     textureImageCacheDirty = true;
649     q->update();
650 }
651
652 /*!
653     Ensures the QSGTextPrivate::doc variable is set to a valid text document
654 */
655 void QSGTextPrivate::ensureDoc()
656 {
657     if (!doc) {
658         Q_Q(QSGText);
659         doc = new QSGTextDocumentWithImageResources(q);
660         doc->setDocumentMargin(0);
661     }
662 }
663
664 /*!
665     Draw \a styleSource as an outline around \a source and return the new image.
666 */
667 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
668 {
669     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
670     img.fill(Qt::transparent);
671
672     QPainter ppm(&img);
673
674     QPoint pos(0, 0);
675     pos += QPoint(-1, 0);
676     ppm.drawPixmap(pos, styleSource);
677     pos += QPoint(2, 0);
678     ppm.drawPixmap(pos, styleSource);
679     pos += QPoint(-1, -1);
680     ppm.drawPixmap(pos, styleSource);
681     pos += QPoint(0, 2);
682     ppm.drawPixmap(pos, styleSource);
683
684     pos += QPoint(0, -1);
685     ppm.drawPixmap(pos, source);
686     ppm.end();
687
688     return img;
689 }
690
691 /*!
692     Draw \a styleSource below \a source at \a yOffset and return the new image.
693 */
694 QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
695 {
696     QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
697     img.fill(Qt::transparent);
698
699     QPainter ppm(&img);
700
701     ppm.drawPixmap(QPoint(0, yOffset), styleSource);
702     ppm.drawPixmap(0, 0, source);
703
704     ppm.end();
705
706     return img;
707 }
708
709 /*!
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.
714     \inherits Item
715
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:
718
719     \qml
720     Text {
721         text: "Hello World!"
722         font.family: "Helvetica"
723         font.pointSize: 24
724         color: "red"
725     }
726     \endqml
727
728     Rich text is defined using HTML-style markup:
729
730     \qml
731     Text {
732         text: "<b>Hello</b> <i>World!</i>"
733     }
734     \endqml
735
736     \image declarative-text.png
737
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).
741
742     The \l elide property can alternatively be used to fit a single line of
743     plain text to a set width.
744
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.
747
748     Text provides read-only text. For editable text, see \l TextEdit.
749
750     \sa {declarative/text/fonts}{Fonts example}
751 */
752 QSGText::QSGText(QSGItem *parent)
753 : QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
754 {
755     Q_D(QSGText);
756     d->init();
757 }
758
759 QSGText::~QSGText()
760 {
761 }
762
763 /*!
764   \qmlproperty bool QtQuick2::Text::clip
765   This property holds whether the text is clipped.
766
767   Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
768
769   If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
770 */
771
772 /*!
773     \qmlproperty bool QtQuick2::Text::smooth
774
775     This property holds whether the text is smoothly scaled or transformed.
776
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
779     performance effect.
780
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.
784 */
785
786 /*!
787     \qmlsignal QtQuick2::Text::onLinkActivated(string link)
788
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.
792
793     \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
794
795     The example code will display the text
796     "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
797
798     Clicking on the highlighted link will output
799     \tt{http://qt.nokia.com link activated} to the console.
800 */
801
802 /*!
803     \qmlproperty string QtQuick2::Text::font.family
804
805     Sets the family name of the font.
806
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.
810 */
811
812 /*!
813     \qmlproperty bool QtQuick2::Text::font.bold
814
815     Sets whether the font weight is bold.
816 */
817
818 /*!
819     \qmlproperty enumeration QtQuick2::Text::font.weight
820
821     Sets the font's weight.
822
823     The weight can be one of:
824     \list
825     \o Font.Light
826     \o Font.Normal - the default
827     \o Font.DemiBold
828     \o Font.Bold
829     \o Font.Black
830     \endlist
831
832     \qml
833     Text { text: "Hello"; font.weight: Font.DemiBold }
834     \endqml
835 */
836
837 /*!
838     \qmlproperty bool QtQuick2::Text::font.italic
839
840     Sets whether the font has an italic style.
841 */
842
843 /*!
844     \qmlproperty bool QtQuick2::Text::font.underline
845
846     Sets whether the text is underlined.
847 */
848
849 /*!
850     \qmlproperty bool QtQuick2::Text::font.strikeout
851
852     Sets whether the font has a strikeout style.
853 */
854
855 /*!
856     \qmlproperty real QtQuick2::Text::font.pointSize
857
858     Sets the font size in points. The point size must be greater than zero.
859 */
860
861 /*!
862     \qmlproperty int QtQuick2::Text::font.pixelSize
863
864     Sets the font size in pixels.
865
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.
868 */
869
870 /*!
871     \qmlproperty real QtQuick2::Text::font.letterSpacing
872
873     Sets the letter spacing for the font.
874
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.
877 */
878
879 /*!
880     \qmlproperty real QtQuick2::Text::font.wordSpacing
881
882     Sets the word spacing for the font.
883
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.
887 */
888
889 /*!
890     \qmlproperty enumeration QtQuick2::Text::font.capitalization
891
892     Sets the capitalization for the text.
893
894     \list
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.
900     \endlist
901
902     \qml
903     Text { text: "Hello"; font.capitalization: Font.AllLowercase }
904     \endqml
905 */
906 QFont QSGText::font() const
907 {
908     Q_D(const QSGText);
909     return d->sourceFont;
910 }
911
912 void QSGText::setFont(const QFont &font)
913 {
914     Q_D(QSGText);
915     if (d->sourceFont == font)
916         return;
917
918     d->sourceFont = font;
919     QFont oldFont = d->font;
920     d->font = font;
921
922     if (d->font.pointSizeF() != -1) {
923         // 0.5pt resolution
924         qreal size = qRound(d->font.pointSizeF()*2.0);
925         d->font.setPointSizeF(size/2.0);
926     }
927
928     if (oldFont != d->font)
929         d->updateLayout();
930
931     emit fontChanged(d->sourceFont);
932 }
933
934 /*!
935     \qmlproperty string QtQuick2::Text::text
936
937     The text to display. Text supports both plain and rich text strings.
938
939     The item will try to automatically determine whether the text should
940     be treated as rich text. This determination is made using Qt::mightBeRichText().
941 */
942 QString QSGText::text() const
943 {
944     Q_D(const QSGText);
945     return d->text;
946 }
947
948 void QSGText::setText(const QString &n)
949 {
950     Q_D(QSGText);
951     if (d->text == n)
952         return;
953
954     d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
955     d->text = n;
956     if (isComponentComplete()) {
957         if (d->richText) {
958             d->ensureDoc();
959             d->doc->setText(n);
960             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
961             d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
962         } else {
963             d->rightToLeftText = d->text.isRightToLeft();
964         }
965         d->determineHorizontalAlignment();
966     }
967     d->updateLayout();
968     emit textChanged(d->text);
969 }
970
971 /*!
972     \qmlproperty color QtQuick2::Text::color
973
974     The text color.
975
976     An example of green text defined using hexadecimal notation:
977     \qml
978     Text {
979         color: "#00FF00"
980         text: "green text"
981     }
982     \endqml
983
984     An example of steel blue text defined using an SVG color name:
985     \qml
986     Text {
987         color: "steelblue"
988         text: "blue text"
989     }
990     \endqml
991 */
992 QColor QSGText::color() const
993 {
994     Q_D(const QSGText);
995     return d->color;
996 }
997
998 void QSGText::setColor(const QColor &color)
999 {
1000     Q_D(QSGText);
1001     if (d->color == color)
1002         return;
1003
1004     d->color = color;
1005     d->invalidateImageCache();
1006     emit colorChanged(d->color);
1007 }
1008 /*!
1009     \qmlproperty enumeration QtQuick2::Text::style
1010
1011     Set an additional text style.
1012
1013     Supported text styles are:
1014     \list
1015     \o Text.Normal - the default
1016     \o Text.Outline
1017     \o Text.Raised
1018     \o Text.Sunken
1019     \endlist
1020
1021     \qml
1022     Row {
1023         Text { font.pointSize: 24; text: "Normal" }
1024         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1025         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1026         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1027     }
1028     \endqml
1029
1030     \image declarative-textstyle.png
1031 */
1032 QSGText::TextStyle QSGText::style() const
1033 {
1034     Q_D(const QSGText);
1035     return d->style;
1036 }
1037
1038 void QSGText::setStyle(QSGText::TextStyle style)
1039 {
1040     Q_D(QSGText);
1041     if (d->style == style)
1042         return;
1043
1044     // changing to/from Normal requires the boundingRect() to change
1045     if (isComponentComplete() && (d->style == Normal || style == Normal))
1046         update();
1047     d->style = style;
1048     d->invalidateImageCache();
1049     emit styleChanged(d->style);
1050 }
1051
1052 /*!
1053     \qmlproperty color QtQuick2::Text::styleColor
1054
1055     Defines the secondary color used by text styles.
1056
1057     \c styleColor is used as the outline color for outlined text, and as the
1058     shadow color for raised or sunken text. If no style has been set, it is not
1059     used at all.
1060
1061     \qml
1062     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1063     \endqml
1064
1065     \sa style
1066  */
1067 QColor QSGText::styleColor() const
1068 {
1069     Q_D(const QSGText);
1070     return d->styleColor;
1071 }
1072
1073 void QSGText::setStyleColor(const QColor &color)
1074 {
1075     Q_D(QSGText);
1076     if (d->styleColor == color)
1077         return;
1078
1079     d->styleColor = color;
1080     d->invalidateImageCache();
1081     emit styleColorChanged(d->styleColor);
1082 }
1083
1084 /*!
1085     \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1086     \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1087     \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1088
1089     Sets the horizontal and vertical alignment of the text within the Text items
1090     width and height. By default, the text is vertically aligned to the top. Horizontal
1091     alignment follows the natural alignment of the text, for example text that is read
1092     from left to right will be aligned to the left.
1093
1094     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1095     \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1096     and \c Text.AlignVCenter.
1097
1098     Note that for a single line of text, the size of the text is the area of the text. In this common case,
1099     all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1100     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1101     that of the parent.
1102
1103     When using the attached property LayoutMirroring::enabled to mirror application
1104     layouts, the horizontal alignment of text will also be mirrored. However, the property
1105     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1106     of Text, use the read-only property \c effectiveHorizontalAlignment.
1107 */
1108 QSGText::HAlignment QSGText::hAlign() const
1109 {
1110     Q_D(const QSGText);
1111     return d->hAlign;
1112 }
1113
1114 void QSGText::setHAlign(HAlignment align)
1115 {
1116     Q_D(QSGText);
1117     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1118     d->hAlignImplicit = false;
1119     if (d->setHAlign(align, forceAlign) && isComponentComplete())
1120         d->updateLayout();
1121 }
1122
1123 void QSGText::resetHAlign()
1124 {
1125     Q_D(QSGText);
1126     d->hAlignImplicit = true;
1127     if (d->determineHorizontalAlignment() && isComponentComplete())
1128         d->updateLayout();
1129 }
1130
1131 QSGText::HAlignment QSGText::effectiveHAlign() const
1132 {
1133     Q_D(const QSGText);
1134     QSGText::HAlignment effectiveAlignment = d->hAlign;
1135     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1136         switch (d->hAlign) {
1137         case QSGText::AlignLeft:
1138             effectiveAlignment = QSGText::AlignRight;
1139             break;
1140         case QSGText::AlignRight:
1141             effectiveAlignment = QSGText::AlignLeft;
1142             break;
1143         default:
1144             break;
1145         }
1146     }
1147     return effectiveAlignment;
1148 }
1149
1150 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1151 {
1152     Q_Q(QSGText);
1153     if (hAlign != alignment || forceAlign) {
1154         QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1155         hAlign = alignment;
1156
1157         emit q->horizontalAlignmentChanged(hAlign);
1158         if (oldEffectiveHAlign != q->effectiveHAlign())
1159             emit q->effectiveHorizontalAlignmentChanged();
1160         return true;
1161     }
1162     return false;
1163 }
1164
1165 bool QSGTextPrivate::determineHorizontalAlignment()
1166 {
1167     Q_Q(QSGText);
1168     if (hAlignImplicit && q->isComponentComplete()) {
1169         bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1170         return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1171     }
1172     return false;
1173 }
1174
1175 void QSGTextPrivate::mirrorChange()
1176 {
1177     Q_Q(QSGText);
1178     if (q->isComponentComplete()) {
1179         if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1180             updateLayout();
1181             emit q->effectiveHorizontalAlignmentChanged();
1182         }
1183     }
1184 }
1185
1186 QTextDocument *QSGTextPrivate::textDocument()
1187 {
1188     return doc;
1189 }
1190
1191 QSGText::VAlignment QSGText::vAlign() const
1192 {
1193     Q_D(const QSGText);
1194     return d->vAlign;
1195 }
1196
1197 void QSGText::setVAlign(VAlignment align)
1198 {
1199     Q_D(QSGText);
1200     if (d->vAlign == align)
1201         return;
1202
1203     d->vAlign = align;
1204     emit verticalAlignmentChanged(align);
1205 }
1206
1207 /*!
1208     \qmlproperty enumeration QtQuick2::Text::wrapMode
1209
1210     Set this property to wrap the text to the Text item's width.  The text will only
1211     wrap if an explicit width has been set.  wrapMode can be one of:
1212
1213     \list
1214     \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1215     \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1216     \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1217     \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
1218     \endlist
1219 */
1220 QSGText::WrapMode QSGText::wrapMode() const
1221 {
1222     Q_D(const QSGText);
1223     return d->wrapMode;
1224 }
1225
1226 void QSGText::setWrapMode(WrapMode mode)
1227 {
1228     Q_D(QSGText);
1229     if (mode == d->wrapMode)
1230         return;
1231
1232     d->wrapMode = mode;
1233     d->updateLayout();
1234
1235     emit wrapModeChanged();
1236 }
1237
1238 /*!
1239     \qmlproperty int QtQuick2::Text::lineCount
1240
1241     Returns the number of lines visible in the text item.
1242
1243     This property is not supported for rich text.
1244
1245     \sa maximumLineCount
1246 */
1247 int QSGText::lineCount() const
1248 {
1249     Q_D(const QSGText);
1250     return d->lineCount;
1251 }
1252
1253 /*!
1254     \qmlproperty bool QtQuick2::Text::truncated
1255
1256     Returns true if the text has been truncated due to \l maximumLineCount
1257     or \l elide.
1258
1259     This property is not supported for rich text.
1260
1261     \sa maximumLineCount, elide
1262 */
1263 bool QSGText::truncated() const
1264 {
1265     Q_D(const QSGText);
1266     return d->truncated;
1267 }
1268
1269 /*!
1270     \qmlproperty int QtQuick2::Text::maximumLineCount
1271
1272     Set this property to limit the number of lines that the text item will show.
1273     If elide is set to Text.ElideRight, the text will be elided appropriately.
1274     By default, this is the value of the largest possible integer.
1275
1276     This property is not supported for rich text.
1277
1278     \sa lineCount, elide
1279 */
1280 int QSGText::maximumLineCount() const
1281 {
1282     Q_D(const QSGText);
1283     return d->maximumLineCount;
1284 }
1285
1286 void QSGText::setMaximumLineCount(int lines)
1287 {
1288     Q_D(QSGText);
1289
1290     d->maximumLineCountValid = lines==INT_MAX ? false : true;
1291     if (d->maximumLineCount != lines) {
1292         d->maximumLineCount = lines;
1293         d->updateLayout();
1294         emit maximumLineCountChanged();
1295     }
1296 }
1297
1298 void QSGText::resetMaximumLineCount()
1299 {
1300     Q_D(QSGText);
1301     setMaximumLineCount(INT_MAX);
1302     d->elidePos = QPointF();
1303     if (d->truncated != false) {
1304         d->truncated = false;
1305         emit truncatedChanged();
1306     }
1307 }
1308
1309 /*!
1310     \qmlproperty enumeration QtQuick2::Text::textFormat
1311
1312     The way the text property should be displayed.
1313
1314     Supported text formats are:
1315
1316     \list
1317     \o Text.AutoText (default)
1318     \o Text.PlainText
1319     \o Text.RichText
1320     \o Text.StyledText
1321     \endlist
1322
1323     If the text format is \c Text.AutoText the text element
1324     will automatically determine whether the text should be treated as
1325     rich text.  This determination is made using Qt::mightBeRichText().
1326
1327     Text.StyledText is an optimized format supporting some basic text
1328     styling markup, in the style of html 3.2:
1329
1330     \code
1331     <b></b> - bold
1332     <i></i> - italic
1333     <br> - new line
1334     <p> - paragraph
1335     <u> - underlined text
1336     <font color="color_name" size="1-7"></font>
1337     <h1> to <h6> - headers
1338     <a href=""> - anchor
1339     <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1340     &gt; &lt; &amp;
1341     \endcode
1342
1343     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1344
1345     \table
1346     \row
1347     \o
1348     \qml
1349 Column {
1350     Text {
1351         font.pointSize: 24
1352         text: "<b>Hello</b> <i>World!</i>"
1353     }
1354     Text {
1355         font.pointSize: 24
1356         textFormat: Text.RichText
1357         text: "<b>Hello</b> <i>World!</i>"
1358     }
1359     Text {
1360         font.pointSize: 24
1361         textFormat: Text.PlainText
1362         text: "<b>Hello</b> <i>World!</i>"
1363     }
1364 }
1365     \endqml
1366     \o \image declarative-textformat.png
1367     \endtable
1368 */
1369 QSGText::TextFormat QSGText::textFormat() const
1370 {
1371     Q_D(const QSGText);
1372     return d->format;
1373 }
1374
1375 void QSGText::setTextFormat(TextFormat format)
1376 {
1377     Q_D(QSGText);
1378     if (format == d->format)
1379         return;
1380     d->format = format;
1381     bool wasRich = d->richText;
1382     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1383
1384     if (!wasRich && d->richText && isComponentComplete()) {
1385         d->ensureDoc();
1386         d->doc->setText(d->text);
1387         d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1388     }
1389
1390     d->updateLayout();
1391
1392     emit textFormatChanged(d->format);
1393 }
1394
1395 /*!
1396     \qmlproperty enumeration QtQuick2::Text::elide
1397
1398     Set this property to elide parts of the text fit to the Text item's width.
1399     The text will only elide if an explicit width has been set.
1400
1401     This property cannot be used with rich text.
1402
1403     Eliding can be:
1404     \list
1405     \o Text.ElideNone  - the default
1406     \o Text.ElideLeft
1407     \o Text.ElideMiddle
1408     \o Text.ElideRight
1409     \endlist
1410
1411     If this property is set to Text.ElideRight, it can be used with multiline
1412     text. The text will only elide if maximumLineCount has been set.
1413
1414     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1415     the first string that fits will be used, otherwise the last will be elided.
1416
1417     Multi-length strings are ordered from longest to shortest, separated by the
1418     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1419 */
1420 QSGText::TextElideMode QSGText::elideMode() const
1421 {
1422     Q_D(const QSGText);
1423     return d->elideMode;
1424 }
1425
1426 void QSGText::setElideMode(QSGText::TextElideMode mode)
1427 {
1428     Q_D(QSGText);
1429     if (mode == d->elideMode)
1430         return;
1431
1432     d->elideMode = mode;
1433     d->updateLayout();
1434
1435     emit elideModeChanged(d->elideMode);
1436 }
1437
1438 /*! \internal */
1439 QRectF QSGText::boundingRect() const
1440 {
1441     Q_D(const QSGText);
1442
1443     QRect rect = d->layedOutTextRect;
1444     if (d->style != Normal)
1445         rect.adjust(-1, 0, 1, 2);
1446
1447     // Could include font max left/right bearings to either side of rectangle.
1448
1449     int h = height();
1450     switch (d->vAlign) {
1451     case AlignTop:
1452         break;
1453     case AlignBottom:
1454         rect.moveTop(h - rect.height());
1455         break;
1456     case AlignVCenter:
1457         rect.moveTop((h - rect.height()) / 2);
1458         break;
1459     }
1460
1461     return QRectF(rect);
1462 }
1463
1464 /*! \internal */
1465 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1466 {
1467     Q_D(QSGText);
1468     if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1469             && (d->wrapMode != QSGText::NoWrap
1470                 || d->elideMode != QSGText::ElideNone
1471                 || d->hAlign != QSGText::AlignLeft)) {
1472         if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1473             // We need to re-elide
1474             d->updateLayout();
1475         } else {
1476             // We just need to re-layout
1477             d->updateSize();
1478         }
1479     }
1480
1481     QSGItem::geometryChanged(newGeometry, oldGeometry);
1482 }
1483
1484 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1485 {
1486     Q_UNUSED(data);
1487     Q_D(QSGText);
1488
1489     if (d->text.isEmpty()) {
1490         delete oldNode;
1491         return 0;
1492     }
1493
1494     QRectF bounds = boundingRect();
1495
1496     // We need to make sure the layout is done in the current thread
1497 #if defined(Q_OS_MAC)
1498     if (d->layoutThread != QThread::currentThread())
1499         d->updateLayout();
1500 #endif
1501
1502     // XXX todo - some styled text can be done by the QSGTextNode
1503     if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1504         bool wasDirty = d->textureImageCacheDirty;
1505         d->textureImageCacheDirty = false;
1506
1507         if (d->imageCache.isNull()) {
1508             delete oldNode;
1509             return 0;
1510         }
1511
1512         QSGImageNode *node = 0;
1513         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1514             delete oldNode;
1515             node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1516             d->texture = new QSGPlainTexture();
1517             wasDirty = true;
1518             d->nodeType = QSGTextPrivate::NodeIsTexture;
1519         } else {
1520             node = static_cast<QSGImageNode *>(oldNode);
1521             Q_ASSERT(d->texture);
1522         }
1523
1524         if (wasDirty) {
1525             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1526             node->setTexture(0);
1527             node->setTexture(d->texture);
1528         }
1529
1530         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1531         node->setSourceRect(QRectF(0, 0, 1, 1));
1532         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1533         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1534         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1535         node->update();
1536
1537         return node;
1538
1539     } else {
1540         QSGTextNode *node = 0;
1541         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1542             delete oldNode;
1543             node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1544             d->nodeType = QSGTextPrivate::NodeIsText;
1545         } else {
1546             node = static_cast<QSGTextNode *>(oldNode);
1547         }
1548
1549         node->deleteContent();
1550         node->setMatrix(QMatrix4x4());
1551
1552         if (d->richText) {
1553
1554             d->ensureDoc();
1555             node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1556
1557         } else {
1558             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1559         }
1560
1561         return node;
1562     }
1563 }
1564
1565 bool QSGText::event(QEvent *e)
1566 {
1567     Q_D(QSGText);
1568     if (e->type() == QEvent::User) {
1569         d->checkImageCache();
1570         return true;
1571     } else {
1572         return QSGImplicitSizeItem::event(e);
1573     }
1574 }
1575
1576 /*!
1577     \qmlproperty real QtQuick2::Text::paintedWidth
1578
1579     Returns the width of the text, including width past the width
1580     which is covered due to insufficient wrapping if WrapMode is set.
1581 */
1582 qreal QSGText::paintedWidth() const
1583 {
1584     Q_D(const QSGText);
1585     return d->paintedSize.width();
1586 }
1587
1588 /*!
1589     \qmlproperty real QtQuick2::Text::paintedHeight
1590
1591     Returns the height of the text, including height past the height
1592     which is covered due to there being more text than fits in the set height.
1593 */
1594 qreal QSGText::paintedHeight() const
1595 {
1596     Q_D(const QSGText);
1597     return d->paintedSize.height();
1598 }
1599
1600 /*!
1601     \qmlproperty real QtQuick2::Text::lineHeight
1602
1603     Sets the line height for the text.
1604     The value can be in pixels or a multiplier depending on lineHeightMode.
1605
1606     The default value is a multiplier of 1.0.
1607     The line height must be a positive value.
1608 */
1609 qreal QSGText::lineHeight() const
1610 {
1611     Q_D(const QSGText);
1612     return d->lineHeight;
1613 }
1614
1615 void QSGText::setLineHeight(qreal lineHeight)
1616 {
1617     Q_D(QSGText);
1618
1619     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1620         return;
1621
1622     d->lineHeight = lineHeight;
1623     d->updateLayout();
1624     emit lineHeightChanged(lineHeight);
1625 }
1626
1627 /*!
1628     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1629
1630     This property determines how the line height is specified.
1631     The possible values are:
1632
1633     \list
1634     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1635        line (as a multiplier). For example, set to 2 for double spacing.
1636     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1637     \endlist
1638 */
1639 QSGText::LineHeightMode QSGText::lineHeightMode() const
1640 {
1641     Q_D(const QSGText);
1642     return d->lineHeightMode;
1643 }
1644
1645 void QSGText::setLineHeightMode(LineHeightMode mode)
1646 {
1647     Q_D(QSGText);
1648     if (mode == d->lineHeightMode)
1649         return;
1650
1651     d->lineHeightMode = mode;
1652     d->updateLayout();
1653
1654     emit lineHeightModeChanged(mode);
1655 }
1656
1657 /*!
1658     Returns the number of resources (images) that are being loaded asynchronously.
1659 */
1660 int QSGText::resourcesLoading() const
1661 {
1662     Q_D(const QSGText);
1663     return d->doc ? d->doc->resourcesLoading() : 0;
1664 }
1665
1666 /*! \internal */
1667 void QSGText::componentComplete()
1668 {
1669     Q_D(QSGText);
1670     QSGItem::componentComplete();
1671     if (d->updateOnComponentComplete) {
1672         d->updateOnComponentComplete = false;
1673         if (d->richText) {
1674             d->ensureDoc();
1675             d->doc->setText(d->text);
1676             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1677             d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1678         } else {
1679             d->rightToLeftText = d->text.isRightToLeft();
1680         }
1681         d->determineHorizontalAlignment();
1682         d->updateLayout();
1683     }
1684 }
1685
1686 /*!  \internal */
1687 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1688 {
1689     Q_D(QSGText);
1690
1691     if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1692         event->setAccepted(false);
1693         d->activeLink.clear();
1694     } else {
1695         d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1696     }
1697
1698     // ### may malfunction if two of the same links are clicked & dragged onto each other)
1699
1700     if (!event->isAccepted())
1701         QSGItem::mousePressEvent(event);
1702
1703 }
1704
1705 /*! \internal */
1706 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1707 {
1708     Q_D(QSGText);
1709
1710         // ### confirm the link, and send a signal out
1711     if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1712         emit linkActivated(d->activeLink);
1713     else
1714         event->setAccepted(false);
1715
1716     if (!event->isAccepted())
1717         QSGItem::mouseReleaseEvent(event);
1718 }
1719
1720 QT_END_NAMESPACE
1721
1722 #include "qsgtext.moc"