Make QSGTextNode back-end for QML's TextInput and TextEdit
[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     <font size="4" color="#ff0000">font size and color</font>
1332     <b>bold</b>
1333     <i>italic</i>
1334     <br>
1335     &gt; &lt; &amp;
1336     \endcode
1337
1338     \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1339
1340     \table
1341     \row
1342     \o
1343     \qml
1344 Column {
1345     Text {
1346         font.pointSize: 24
1347         text: "<b>Hello</b> <i>World!</i>"
1348     }
1349     Text {
1350         font.pointSize: 24
1351         textFormat: Text.RichText
1352         text: "<b>Hello</b> <i>World!</i>"
1353     }
1354     Text {
1355         font.pointSize: 24
1356         textFormat: Text.PlainText
1357         text: "<b>Hello</b> <i>World!</i>"
1358     }
1359 }
1360     \endqml
1361     \o \image declarative-textformat.png
1362     \endtable
1363 */
1364 QSGText::TextFormat QSGText::textFormat() const
1365 {
1366     Q_D(const QSGText);
1367     return d->format;
1368 }
1369
1370 void QSGText::setTextFormat(TextFormat format)
1371 {
1372     Q_D(QSGText);
1373     if (format == d->format)
1374         return;
1375     d->format = format;
1376     bool wasRich = d->richText;
1377     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
1378
1379     if (!wasRich && d->richText && isComponentComplete()) {
1380         d->ensureDoc();
1381         d->doc->setText(d->text);
1382         d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1383     }
1384
1385     d->updateLayout();
1386
1387     emit textFormatChanged(d->format);
1388 }
1389
1390 /*!
1391     \qmlproperty enumeration QtQuick2::Text::elide
1392
1393     Set this property to elide parts of the text fit to the Text item's width.
1394     The text will only elide if an explicit width has been set.
1395
1396     This property cannot be used with rich text.
1397
1398     Eliding can be:
1399     \list
1400     \o Text.ElideNone  - the default
1401     \o Text.ElideLeft
1402     \o Text.ElideMiddle
1403     \o Text.ElideRight
1404     \endlist
1405
1406     If this property is set to Text.ElideRight, it can be used with multiline
1407     text. The text will only elide if maximumLineCount has been set.
1408
1409     If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1410     the first string that fits will be used, otherwise the last will be elided.
1411
1412     Multi-length strings are ordered from longest to shortest, separated by the
1413     Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1414 */
1415 QSGText::TextElideMode QSGText::elideMode() const
1416 {
1417     Q_D(const QSGText);
1418     return d->elideMode;
1419 }
1420
1421 void QSGText::setElideMode(QSGText::TextElideMode mode)
1422 {
1423     Q_D(QSGText);
1424     if (mode == d->elideMode)
1425         return;
1426
1427     d->elideMode = mode;
1428     d->updateLayout();
1429
1430     emit elideModeChanged(d->elideMode);
1431 }
1432
1433 /*! \internal */
1434 QRectF QSGText::boundingRect() const
1435 {
1436     Q_D(const QSGText);
1437
1438     QRect rect = d->layedOutTextRect;
1439     if (d->style != Normal)
1440         rect.adjust(-1, 0, 1, 2);
1441
1442     // Could include font max left/right bearings to either side of rectangle.
1443
1444     int h = height();
1445     switch (d->vAlign) {
1446     case AlignTop:
1447         break;
1448     case AlignBottom:
1449         rect.moveTop(h - rect.height());
1450         break;
1451     case AlignVCenter:
1452         rect.moveTop((h - rect.height()) / 2);
1453         break;
1454     }
1455
1456     return QRectF(rect);
1457 }
1458
1459 /*! \internal */
1460 void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1461 {
1462     Q_D(QSGText);
1463     if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
1464             && (d->wrapMode != QSGText::NoWrap
1465                 || d->elideMode != QSGText::ElideNone
1466                 || d->hAlign != QSGText::AlignLeft)) {
1467         if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
1468             // We need to re-elide
1469             d->updateLayout();
1470         } else {
1471             // We just need to re-layout
1472             d->updateSize();
1473         }
1474     }
1475
1476     QSGItem::geometryChanged(newGeometry, oldGeometry);
1477 }
1478
1479 QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1480 {
1481     Q_UNUSED(data);
1482     Q_D(QSGText);
1483
1484     if (d->text.isEmpty()) {
1485         delete oldNode;
1486         return 0;
1487     }
1488
1489     QRectF bounds = boundingRect();
1490
1491     // We need to make sure the layout is done in the current thread
1492 #if defined(Q_OS_MAC)
1493     if (d->layoutThread != QThread::currentThread())
1494         d->updateLayout();
1495 #endif
1496
1497     // XXX todo - some styled text can be done by the QSGTextNode
1498     if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
1499         bool wasDirty = d->textureImageCacheDirty;
1500         d->textureImageCacheDirty = false;
1501
1502         if (d->imageCache.isNull()) {
1503             delete oldNode;
1504             return 0;
1505         }
1506
1507         QSGImageNode *node = 0;
1508         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
1509             delete oldNode;
1510             node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1511             d->texture = new QSGPlainTexture();
1512             wasDirty = true;
1513             d->nodeType = QSGTextPrivate::NodeIsTexture;
1514         } else {
1515             node = static_cast<QSGImageNode *>(oldNode);
1516             Q_ASSERT(d->texture);
1517         }
1518
1519         if (wasDirty) {
1520             qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
1521             node->setTexture(0);
1522             node->setTexture(d->texture);
1523         }
1524
1525         node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
1526         node->setSourceRect(QRectF(0, 0, 1, 1));
1527         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1528         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1529         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1530         node->update();
1531
1532         return node;
1533
1534     } else {
1535         QSGTextNode *node = 0;
1536         if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
1537             delete oldNode;
1538             node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1539             d->nodeType = QSGTextPrivate::NodeIsText;
1540         } else {
1541             node = static_cast<QSGTextNode *>(oldNode);
1542         }
1543
1544         node->deleteContent();
1545         node->setMatrix(QMatrix4x4());
1546
1547         if (d->richText) {
1548
1549             d->ensureDoc();
1550             node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
1551
1552         } else {
1553             node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
1554         }
1555
1556         return node;
1557     }
1558 }
1559
1560 bool QSGText::event(QEvent *e)
1561 {
1562     Q_D(QSGText);
1563     if (e->type() == QEvent::User) {
1564         d->checkImageCache();
1565         return true;
1566     } else {
1567         return QSGImplicitSizeItem::event(e);
1568     }
1569 }
1570
1571 /*!
1572     \qmlproperty real QtQuick2::Text::paintedWidth
1573
1574     Returns the width of the text, including width past the width
1575     which is covered due to insufficient wrapping if WrapMode is set.
1576 */
1577 qreal QSGText::paintedWidth() const
1578 {
1579     Q_D(const QSGText);
1580     return d->paintedSize.width();
1581 }
1582
1583 /*!
1584     \qmlproperty real QtQuick2::Text::paintedHeight
1585
1586     Returns the height of the text, including height past the height
1587     which is covered due to there being more text than fits in the set height.
1588 */
1589 qreal QSGText::paintedHeight() const
1590 {
1591     Q_D(const QSGText);
1592     return d->paintedSize.height();
1593 }
1594
1595 /*!
1596     \qmlproperty real QtQuick2::Text::lineHeight
1597
1598     Sets the line height for the text.
1599     The value can be in pixels or a multiplier depending on lineHeightMode.
1600
1601     The default value is a multiplier of 1.0.
1602     The line height must be a positive value.
1603 */
1604 qreal QSGText::lineHeight() const
1605 {
1606     Q_D(const QSGText);
1607     return d->lineHeight;
1608 }
1609
1610 void QSGText::setLineHeight(qreal lineHeight)
1611 {
1612     Q_D(QSGText);
1613
1614     if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
1615         return;
1616
1617     d->lineHeight = lineHeight;
1618     d->updateLayout();
1619     emit lineHeightChanged(lineHeight);
1620 }
1621
1622 /*!
1623     \qmlproperty enumeration QtQuick2::Text::lineHeightMode
1624
1625     This property determines how the line height is specified.
1626     The possible values are:
1627
1628     \list
1629     \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
1630        line (as a multiplier). For example, set to 2 for double spacing.
1631     \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
1632     \endlist
1633 */
1634 QSGText::LineHeightMode QSGText::lineHeightMode() const
1635 {
1636     Q_D(const QSGText);
1637     return d->lineHeightMode;
1638 }
1639
1640 void QSGText::setLineHeightMode(LineHeightMode mode)
1641 {
1642     Q_D(QSGText);
1643     if (mode == d->lineHeightMode)
1644         return;
1645
1646     d->lineHeightMode = mode;
1647     d->updateLayout();
1648
1649     emit lineHeightModeChanged(mode);
1650 }
1651
1652 /*!
1653     Returns the number of resources (images) that are being loaded asynchronously.
1654 */
1655 int QSGText::resourcesLoading() const
1656 {
1657     Q_D(const QSGText);
1658     return d->doc ? d->doc->resourcesLoading() : 0;
1659 }
1660
1661 /*! \internal */
1662 void QSGText::componentComplete()
1663 {
1664     Q_D(QSGText);
1665     QSGItem::componentComplete();
1666     if (d->updateOnComponentComplete) {
1667         d->updateOnComponentComplete = false;
1668         if (d->richText) {
1669             d->ensureDoc();
1670             d->doc->setText(d->text);
1671             d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1672             d->richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
1673         } else {
1674             d->rightToLeftText = d->text.isRightToLeft();
1675         }
1676         d->determineHorizontalAlignment();
1677         d->updateLayout();
1678     }
1679 }
1680
1681 /*!  \internal */
1682 void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
1683 {
1684     Q_D(QSGText);
1685
1686     if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
1687         event->setAccepted(false);
1688         d->activeLink.clear();
1689     } else {
1690         d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
1691     }
1692
1693     // ### may malfunction if two of the same links are clicked & dragged onto each other)
1694
1695     if (!event->isAccepted())
1696         QSGItem::mousePressEvent(event);
1697
1698 }
1699
1700 /*! \internal */
1701 void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1702 {
1703     Q_D(QSGText);
1704
1705         // ### confirm the link, and send a signal out
1706     if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
1707         emit linkActivated(d->activeLink);
1708     else
1709         event->setAccepted(false);
1710
1711     if (!event->isAccepted())
1712         QSGItem::mouseReleaseEvent(event);
1713 }
1714
1715 QT_END_NAMESPACE
1716
1717 #include "qsgtext.moc"