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