Text format AutoText should use StyledText instead of RichText.
[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/qevent.h>
54 #include <QtGui/qabstracttextdocumentlayout.h>
55 #include <QtGui/qpainter.h>
56 #include <QtGui/qtextdocument.h>
57 #include <QtGui/qtextobject.h>
58 #include <QtGui/qtextcursor.h>
59 #include <QtGui/qguiapplication.h>
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), styledText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
108   requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
109   layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
110   doc(0), nodeType(NodeIsNull)
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 (!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 styled 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;
955     d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
956     d->text = n;
957     if (isComponentComplete()) {
958         if (d->richText) {
959             d->ensureDoc();
960             d->doc->setText(n);
961             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
962             d->richTextAsImage = enableImageCache();
963         } else {
964             d->rightToLeftText = d->text.isRightToLeft();
965         }
966         d->determineHorizontalAlignment();
967     }
968     d->updateLayout();
969     emit textChanged(d->text);
970 }
971
972 /*!
973     \qmlproperty color QtQuick2::Text::color
974
975     The text color.
976
977     An example of green text defined using hexadecimal notation:
978     \qml
979     Text {
980         color: "#00FF00"
981         text: "green text"
982     }
983     \endqml
984
985     An example of steel blue text defined using an SVG color name:
986     \qml
987     Text {
988         color: "steelblue"
989         text: "blue text"
990     }
991     \endqml
992 */
993 QColor QSGText::color() const
994 {
995     Q_D(const QSGText);
996     return d->color;
997 }
998
999 void QSGText::setColor(const QColor &color)
1000 {
1001     Q_D(QSGText);
1002     if (d->color == color)
1003         return;
1004
1005     d->color = color;
1006     d->invalidateImageCache();
1007     emit colorChanged(d->color);
1008 }
1009 /*!
1010     \qmlproperty enumeration QtQuick2::Text::style
1011
1012     Set an additional text style.
1013
1014     Supported text styles are:
1015     \list
1016     \o Text.Normal - the default
1017     \o Text.Outline
1018     \o Text.Raised
1019     \o Text.Sunken
1020     \endlist
1021
1022     \qml
1023     Row {
1024         Text { font.pointSize: 24; text: "Normal" }
1025         Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1026         Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1027         Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1028     }
1029     \endqml
1030
1031     \image declarative-textstyle.png
1032 */
1033 QSGText::TextStyle QSGText::style() const
1034 {
1035     Q_D(const QSGText);
1036     return d->style;
1037 }
1038
1039 void QSGText::setStyle(QSGText::TextStyle style)
1040 {
1041     Q_D(QSGText);
1042     if (d->style == style)
1043         return;
1044
1045     // changing to/from Normal requires the boundingRect() to change
1046     if (isComponentComplete() && (d->style == Normal || style == Normal))
1047         update();
1048     d->style = style;
1049     d->invalidateImageCache();
1050     emit styleChanged(d->style);
1051 }
1052
1053 /*!
1054     \qmlproperty color QtQuick2::Text::styleColor
1055
1056     Defines the secondary color used by text styles.
1057
1058     \c styleColor is used as the outline color for outlined text, and as the
1059     shadow color for raised or sunken text. If no style has been set, it is not
1060     used at all.
1061
1062     \qml
1063     Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1064     \endqml
1065
1066     \sa style
1067  */
1068 QColor QSGText::styleColor() const
1069 {
1070     Q_D(const QSGText);
1071     return d->styleColor;
1072 }
1073
1074 void QSGText::setStyleColor(const QColor &color)
1075 {
1076     Q_D(QSGText);
1077     if (d->styleColor == color)
1078         return;
1079
1080     d->styleColor = color;
1081     d->invalidateImageCache();
1082     emit styleColorChanged(d->styleColor);
1083 }
1084
1085 /*!
1086     \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1087     \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1088     \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1089
1090     Sets the horizontal and vertical alignment of the text within the Text items
1091     width and height. By default, the text is vertically aligned to the top. Horizontal
1092     alignment follows the natural alignment of the text, for example text that is read
1093     from left to right will be aligned to the left.
1094
1095     The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1096     \c Text.AlignJustify.  The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1097     and \c Text.AlignVCenter.
1098
1099     Note that for a single line of text, the size of the text is the area of the text. In this common case,
1100     all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1101     need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1102     that of the parent.
1103
1104     When using the attached property LayoutMirroring::enabled to mirror application
1105     layouts, the horizontal alignment of text will also be mirrored. However, the property
1106     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1107     of Text, use the read-only property \c effectiveHorizontalAlignment.
1108 */
1109 QSGText::HAlignment QSGText::hAlign() const
1110 {
1111     Q_D(const QSGText);
1112     return d->hAlign;
1113 }
1114
1115 void QSGText::setHAlign(HAlignment align)
1116 {
1117     Q_D(QSGText);
1118     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1119     d->hAlignImplicit = false;
1120     if (d->setHAlign(align, forceAlign) && isComponentComplete())
1121         d->updateLayout();
1122 }
1123
1124 void QSGText::resetHAlign()
1125 {
1126     Q_D(QSGText);
1127     d->hAlignImplicit = true;
1128     if (d->determineHorizontalAlignment() && isComponentComplete())
1129         d->updateLayout();
1130 }
1131
1132 QSGText::HAlignment QSGText::effectiveHAlign() const
1133 {
1134     Q_D(const QSGText);
1135     QSGText::HAlignment effectiveAlignment = d->hAlign;
1136     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1137         switch (d->hAlign) {
1138         case QSGText::AlignLeft:
1139             effectiveAlignment = QSGText::AlignRight;
1140             break;
1141         case QSGText::AlignRight:
1142             effectiveAlignment = QSGText::AlignLeft;
1143             break;
1144         default:
1145             break;
1146         }
1147     }
1148     return effectiveAlignment;
1149 }
1150
1151 bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
1152 {
1153     Q_Q(QSGText);
1154     if (hAlign != alignment || forceAlign) {
1155         QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1156         hAlign = alignment;
1157
1158         emit q->horizontalAlignmentChanged(hAlign);
1159         if (oldEffectiveHAlign != q->effectiveHAlign())
1160             emit q->effectiveHorizontalAlignmentChanged();
1161         return true;
1162     }
1163     return false;
1164 }
1165
1166 bool QSGTextPrivate::determineHorizontalAlignment()
1167 {
1168     Q_Q(QSGText);
1169     if (hAlignImplicit && q->isComponentComplete()) {
1170         bool alignToRight = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
1171         return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
1172     }
1173     return false;
1174 }
1175
1176 void QSGTextPrivate::mirrorChange()
1177 {
1178     Q_Q(QSGText);
1179     if (q->isComponentComplete()) {
1180         if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
1181             updateLayout();
1182             emit q->effectiveHorizontalAlignmentChanged();
1183         }
1184     }
1185 }
1186
1187 QTextDocument *QSGTextPrivate::textDocument()
1188 {
1189     return doc;
1190 }
1191
1192 QSGText::VAlignment QSGText::vAlign() const
1193 {
1194     Q_D(const QSGText);
1195     return d->vAlign;
1196 }
1197
1198 void QSGText::setVAlign(VAlignment align)
1199 {
1200     Q_D(QSGText);
1201     if (d->vAlign == align)
1202         return;
1203
1204     d->vAlign = align;
1205     emit verticalAlignmentChanged(align);
1206 }
1207
1208 /*!
1209     \qmlproperty enumeration QtQuick2::Text::wrapMode
1210
1211     Set this property to wrap the text to the Text item's width.  The text will only
1212     wrap if an explicit width has been set.  wrapMode can be one of:
1213
1214     \list
1215     \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1216     \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1217     \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1218     \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
1219     \endlist
1220 */
1221 QSGText::WrapMode QSGText::wrapMode() const
1222 {
1223     Q_D(const QSGText);
1224     return d->wrapMode;
1225 }
1226
1227 void QSGText::setWrapMode(WrapMode mode)
1228 {
1229     Q_D(QSGText);
1230     if (mode == d->wrapMode)
1231         return;
1232
1233     d->wrapMode = mode;
1234     d->updateLayout();
1235
1236     emit wrapModeChanged();
1237 }
1238
1239 /*!
1240     \qmlproperty int QtQuick2::Text::lineCount
1241
1242     Returns the number of lines visible in the text item.
1243
1244     This property is not supported for rich text.
1245
1246     \sa maximumLineCount
1247 */
1248 int QSGText::lineCount() const
1249 {
1250     Q_D(const QSGText);
1251     return d->lineCount;
1252 }
1253
1254 /*!
1255     \qmlproperty bool QtQuick2::Text::truncated
1256
1257     Returns true if the text has been truncated due to \l maximumLineCount
1258     or \l elide.
1259
1260     This property is not supported for rich text.
1261
1262     \sa maximumLineCount, elide
1263 */
1264 bool QSGText::truncated() const
1265 {
1266     Q_D(const QSGText);
1267     return d->truncated;
1268 }
1269
1270 /*!
1271     \qmlproperty int QtQuick2::Text::maximumLineCount
1272
1273     Set this property to limit the number of lines that the text item will show.
1274     If elide is set to Text.ElideRight, the text will be elided appropriately.
1275     By default, this is the value of the largest possible integer.
1276
1277     This property is not supported for rich text.
1278
1279     \sa lineCount, elide
1280 */
1281 int QSGText::maximumLineCount() const
1282 {
1283     Q_D(const QSGText);
1284     return d->maximumLineCount;
1285 }
1286
1287 void QSGText::setMaximumLineCount(int lines)
1288 {
1289     Q_D(QSGText);
1290
1291     d->maximumLineCountValid = lines==INT_MAX ? false : true;
1292     if (d->maximumLineCount != lines) {
1293         d->maximumLineCount = lines;
1294         d->updateLayout();
1295         emit maximumLineCountChanged();
1296     }
1297 }
1298
1299 void QSGText::resetMaximumLineCount()
1300 {
1301     Q_D(QSGText);
1302     setMaximumLineCount(INT_MAX);
1303     d->elidePos = QPointF();
1304     if (d->truncated != false) {
1305         d->truncated = false;
1306         emit truncatedChanged();
1307     }
1308 }
1309
1310 /*!
1311     \qmlproperty enumeration QtQuick2::Text::textFormat
1312
1313     The way the text property should be displayed.
1314
1315     Supported text formats are:
1316
1317     \list
1318     \o Text.AutoText (default)
1319     \o Text.PlainText
1320     \o Text.StyledText
1321     \o Text.RichText
1322     \endlist
1323
1324     If the text format is \c Text.AutoText the text element
1325     will automatically determine whether the text should be treated as
1326     styled text.  This determination is made using Qt::mightBeRichText().
1327
1328     Text.StyledText is an optimized format supporting some basic text
1329     styling markup, in the style of html 3.2:
1330
1331     \code
1332     <b></b> - bold
1333     <i></i> - italic
1334     <br> - new line
1335     <p> - paragraph
1336     <u> - underlined text
1337     <font color="color_name" size="1-7"></font>
1338     <h1> to <h6> - headers
1339     <a href=""> - anchor
1340     <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1341     &gt; &lt; &amp;
1342     \endcode
1343
1344     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1345
1346     \table
1347     \row
1348     \o
1349     \qml
1350 Column {
1351     Text {
1352         font.pointSize: 24
1353         text: "<b>Hello</b> <i>World!</i>"
1354     }
1355     Text {
1356         font.pointSize: 24
1357         textFormat: Text.RichText
1358         text: "<b>Hello</b> <i>World!</i>"
1359     }
1360     Text {
1361         font.pointSize: 24
1362         textFormat: Text.PlainText
1363         text: "<b>Hello</b> <i>World!</i>"
1364     }
1365 }
1366     \endqml
1367     \o \image declarative-textformat.png
1368     \endtable
1369 */
1370 QSGText::TextFormat QSGText::textFormat() const
1371 {
1372     Q_D(const QSGText);
1373     return d->format;
1374 }
1375
1376 void QSGText::setTextFormat(TextFormat format)
1377 {
1378     Q_D(QSGText);
1379     if (format == d->format)
1380         return;
1381     d->format = format;
1382     bool wasRich = d->richText;
1383     d->richText = format == RichText;
1384     d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1385
1386     if (!wasRich && d->richText && isComponentComplete()) {
1387         d->ensureDoc();
1388         d->doc->setText(d->text);
1389         d->richTextAsImage = enableImageCache();
1390     }
1391
1392     d->updateLayout();
1393
1394     emit textFormatChanged(d->format);
1395 }
1396
1397 /*!
1398     \qmlproperty enumeration QtQuick2::Text::elide
1399
1400     Set this property to elide parts of the text fit to the Text item's width.
1401     The text will only elide if an explicit width has been set.
1402
1403     This property cannot be used with rich text.
1404
1405     Eliding can be:
1406     \list
1407     \o Text.ElideNone  - the default
1408     \o Text.ElideLeft
1409     \o Text.ElideMiddle
1410     \o Text.ElideRight
1411     \endlist
1412
1413     If this property is set to Text.ElideRight, it can be used with multiline
1414     text. The text will only elide if maximumLineCount has been set.
1415
1416     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1417     the first string that fits will be used, otherwise the last will be elided.
1418
1419     Multi-length strings are ordered from longest to shortest, separated by the
1420     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1421 */
1422 QSGText::TextElideMode QSGText::elideMode() const
1423 {
1424     Q_D(const QSGText);
1425     return d->elideMode;
1426 }
1427
1428 void QSGText::setElideMode(QSGText::TextElideMode mode)
1429 {
1430     Q_D(QSGText);
1431     if (mode == d->elideMode)
1432         return;
1433
1434     d->elideMode = mode;
1435     d->updateLayout();
1436
1437     emit elideModeChanged(d->elideMode);
1438 }
1439
1440 /*! \internal */
1441 QRectF QSGText::boundingRect() const
1442 {
1443     Q_D(const QSGText);
1444
1445     QRect rect = d->layedOutTextRect;
1446     if (d->style != Normal)
1447         rect.adjust(-1, 0, 1, 2);
1448
1449     // Could include font max left/right bearings to either side of rectangle.
1450
1451     int h = height();
1452     switch (d->vAlign) {
1453     case AlignTop:
1454         break;
1455     case AlignBottom:
1456         rect.moveTop(h - rect.height());
1457         break;
1458     case AlignVCenter:
1459         rect.moveTop((h - rect.height()) / 2);
1460         break;
1461     }
1462
1463     return QRectF(rect);
1464 }
1465
1466 /*! \internal */
1467 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1468 {
1469     Q_D(QSGText);
1470     if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1471             && (d->wrapMode != QSGText::NoWrap
1472                 || d->elideMode != QSGText::ElideNone
1473                 || d->hAlign != QSGText::AlignLeft)) {
1474         if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1475             // We need to re-elide
1476             d->updateLayout();
1477         } else {
1478             // We just need to re-layout
1479             d->updateSize();
1480         }
1481     }
1482
1483     QSGItem::geometryChanged(newGeometry, oldGeometry);
1484 }
1485
1486 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1487 {
1488     Q_UNUSED(data);
1489     Q_D(QSGText);
1490
1491     if (d->text.isEmpty()) {
1492         delete oldNode;
1493         return 0;
1494     }
1495
1496     QRectF bounds = boundingRect();
1497
1498     // We need to make sure the layout is done in the current thread
1499 #if defined(Q_OS_MAC)
1500     if (d->layoutThread != QThread::currentThread())
1501         d->updateLayout();
1502 #endif
1503
1504     // XXX todo - some styled text can be done by the QSGTextNode
1505     if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1506         bool wasDirty = d->textureImageCacheDirty;
1507         d->textureImageCacheDirty = false;
1508
1509         if (d->imageCache.isNull()) {
1510             delete oldNode;
1511             return 0;
1512         }
1513
1514         QSGImageNode *node = 0;
1515         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1516             delete oldNode;
1517             node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1518             d->texture = new QSGPlainTexture();
1519             wasDirty = true;
1520             d->nodeType = QSGTextPrivate::NodeIsTexture;
1521         } else {
1522             node = static_cast<QSGImageNode *>(oldNode);
1523             Q_ASSERT(d->texture);
1524         }
1525
1526         if (wasDirty) {
1527             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1528             node->setTexture(0);
1529             node->setTexture(d->texture);
1530         }
1531
1532         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1533         node->setSourceRect(QRectF(0, 0, 1, 1));
1534         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1535         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1536         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1537         node->update();
1538
1539         return node;
1540
1541     } else {
1542         QSGTextNode *node = 0;
1543         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1544             delete oldNode;
1545             node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1546             d->nodeType = QSGTextPrivate::NodeIsText;
1547         } else {
1548             node = static_cast<QSGTextNode *>(oldNode);
1549         }
1550
1551         node->deleteContent();
1552         node->setMatrix(QMatrix4x4());
1553
1554         if (d->richText) {
1555
1556             d->ensureDoc();
1557             node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1558
1559         } else {
1560             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1561         }
1562
1563         return node;
1564     }
1565 }
1566
1567 bool QSGText::event(QEvent *e)
1568 {
1569     Q_D(QSGText);
1570     if (e->type() == QEvent::User) {
1571         d->checkImageCache();
1572         return true;
1573     } else {
1574         return QSGImplicitSizeItem::event(e);
1575     }
1576 }
1577
1578 /*!
1579     \qmlproperty real QtQuick2::Text::paintedWidth
1580
1581     Returns the width of the text, including width past the width
1582     which is covered due to insufficient wrapping if WrapMode is set.
1583 */
1584 qreal QSGText::paintedWidth() const
1585 {
1586     Q_D(const QSGText);
1587     return d->paintedSize.width();
1588 }
1589
1590 /*!
1591     \qmlproperty real QtQuick2::Text::paintedHeight
1592
1593     Returns the height of the text, including height past the height
1594     which is covered due to there being more text than fits in the set height.
1595 */
1596 qreal QSGText::paintedHeight() const
1597 {
1598     Q_D(const QSGText);
1599     return d->paintedSize.height();
1600 }
1601
1602 /*!
1603     \qmlproperty real QtQuick2::Text::lineHeight
1604
1605     Sets the line height for the text.
1606     The value can be in pixels or a multiplier depending on lineHeightMode.
1607
1608     The default value is a multiplier of 1.0.
1609     The line height must be a positive value.
1610 */
1611 qreal QSGText::lineHeight() const
1612 {
1613     Q_D(const QSGText);
1614     return d->lineHeight;
1615 }
1616
1617 void QSGText::setLineHeight(qreal lineHeight)
1618 {
1619     Q_D(QSGText);
1620
1621     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1622         return;
1623
1624     d->lineHeight = lineHeight;
1625     d->updateLayout();
1626     emit lineHeightChanged(lineHeight);
1627 }
1628
1629 /*!
1630     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1631
1632     This property determines how the line height is specified.
1633     The possible values are:
1634
1635     \list
1636     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1637        line (as a multiplier). For example, set to 2 for double spacing.
1638     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1639     \endlist
1640 */
1641 QSGText::LineHeightMode QSGText::lineHeightMode() const
1642 {
1643     Q_D(const QSGText);
1644     return d->lineHeightMode;
1645 }
1646
1647 void QSGText::setLineHeightMode(LineHeightMode mode)
1648 {
1649     Q_D(QSGText);
1650     if (mode == d->lineHeightMode)
1651         return;
1652
1653     d->lineHeightMode = mode;
1654     d->updateLayout();
1655
1656     emit lineHeightModeChanged(mode);
1657 }
1658
1659 /*!
1660     Returns the number of resources (images) that are being loaded asynchronously.
1661 */
1662 int QSGText::resourcesLoading() const
1663 {
1664     Q_D(const QSGText);
1665     return d->doc ? d->doc->resourcesLoading() : 0;
1666 }
1667
1668 /*! \internal */
1669 void QSGText::componentComplete()
1670 {
1671     Q_D(QSGText);
1672     QSGItem::componentComplete();
1673     if (d->updateOnComponentComplete) {
1674         d->updateOnComponentComplete = false;
1675         if (d->richText) {
1676             d->ensureDoc();
1677             d->doc->setText(d->text);
1678             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1679             d->richTextAsImage = enableImageCache();
1680         } else {
1681             d->rightToLeftText = d->text.isRightToLeft();
1682         }
1683         d->determineHorizontalAlignment();
1684         d->updateLayout();
1685     }
1686 }
1687
1688
1689 QString QSGTextPrivate::anchorAt(const QPointF &mousePos)
1690 {
1691     if (styledText) {
1692         for (int i = 0; i < layout.lineCount(); ++i) {
1693             QTextLine line = layout.lineAt(i);
1694             if (line.naturalTextRect().contains(mousePos)) {
1695                 int charPos = line.xToCursor(mousePos.x());
1696                 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
1697                     if (formatRange.format.isAnchor()
1698                             && charPos >= formatRange.start
1699                             && charPos <= formatRange.start + formatRange.length) {
1700                         return formatRange.format.anchorHref();
1701                     }
1702                 }
1703                 break;
1704             }
1705         }
1706     }
1707     return QString();
1708 }
1709
1710 bool QSGTextPrivate::isLinkActivatedConnected()
1711 {
1712     static int idx = this->signalIndex("linkActivated(QString)");
1713     return this->isSignalConnected(idx);
1714 }
1715
1716 /*!  \internal */
1717 void QSGText::mousePressEvent(QMouseEvent *event)
1718 {
1719     Q_D(QSGText);
1720
1721     if (d->isLinkActivatedConnected()) {
1722         if (d->styledText)
1723             d->activeLink = d->anchorAt(event->localPos());
1724         else if (d->richText && d->doc)
1725             d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
1726     }
1727
1728     if (d->activeLink.isEmpty())
1729         event->setAccepted(false);
1730
1731     // ### may malfunction if two of the same links are clicked & dragged onto each other)
1732
1733     if (!event->isAccepted())
1734         QSGItem::mousePressEvent(event);
1735
1736 }
1737
1738 /*! \internal */
1739 void QSGText::mouseReleaseEvent(QMouseEvent *event)
1740 {
1741     Q_D(QSGText);
1742
1743     // ### confirm the link, and send a signal out
1744
1745     QString link;
1746     if (d->isLinkActivatedConnected()) {
1747         if (d->styledText)
1748             link = d->anchorAt(event->localPos());
1749         else if (d->richText && d->doc)
1750             link = d->doc->documentLayout()->anchorAt(event->localPos());
1751     }
1752
1753     if (!link.isEmpty() && d->activeLink == link)
1754         emit linkActivated(d->activeLink);
1755     else
1756         event->setAccepted(false);
1757
1758     if (!event->isAccepted())
1759         QSGItem::mouseReleaseEvent(event);
1760 }
1761
1762 QT_END_NAMESPACE
1763
1764 #include "qsgtext.moc"