1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquicktext_p.h"
43 #include "qquicktext_p_p.h"
45 #include <QtQuick/private/qsgcontext_p.h>
46 #include <private/qsgadaptationlayer_p.h>
47 #include "qquicktextnode_p.h"
48 #include "qquickimage_p_p.h"
49 #include <QtQuick/private/qsgtexture_p.h>
51 #include <QtDeclarative/qdeclarativeinfo.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qabstracttextdocumentlayout.h>
54 #include <QtGui/qpainter.h>
55 #include <QtGui/qtextdocument.h>
56 #include <QtGui/qtextobject.h>
57 #include <QtGui/qtextcursor.h>
58 #include <QtGui/qguiapplication.h>
59 #include <QtGui/qinputpanel.h>
61 #include <private/qtextengine_p.h>
62 #include <private/qdeclarativestyledtext_p.h>
63 #include <QtQuick/private/qdeclarativepixmapcache_p.h>
70 extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
72 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
73 DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
75 const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
77 QQuickTextPrivate::QQuickTextPrivate()
78 : color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
79 vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
80 format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
81 lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
82 maximumLineCountValid(false), fontSizeMode(QQuickText::FixedSize), minimumPixelSize(12),
83 minimumPointSize(12), imageCache(0), texture(0),
84 imageCacheDirty(false), updateOnComponentComplete(true),
85 richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true),
86 disableDistanceField(false), internalWidthUpdate(false),
87 requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
88 layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
89 needToUpdateLayout(false), naturalWidth(0), doc(0), elideLayout(0), textLine(0), nodeType(NodeIsNull),
90 updateType(UpdatePaintNode), nbActiveDownloads(0)
93 , layoutThread(0), paintingThread(0)
96 cacheAllTextAsImage = enableImageCache();
97 disableDistanceField = qmlDisableDistanceField();
100 void QQuickTextPrivate::init()
103 q->setAcceptedMouseButtons(Qt::LeftButton);
104 q->setFlag(QQuickItem::ItemHasContents);
107 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
108 : QTextDocument(parent), outstanding(0)
110 setUndoRedoEnabled(false);
111 documentLayout()->registerHandler(QTextFormat::ImageObject, this);
114 QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
116 if (!m_resources.isEmpty())
117 qDeleteAll(m_resources);
120 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
122 QDeclarativeContext *context = qmlContext(parent());
123 QUrl url = m_baseUrl.resolved(name);
125 if (type == QTextDocument::ImageResource) {
126 QDeclarativePixmap *p = loadPixmap(context, url);
130 return QTextDocument::loadResource(type,url); // The *resolved* URL
133 void QQuickTextDocumentWithImageResources::requestFinished()
136 if (outstanding == 0) {
137 markContentsDirty(0, characterCount());
142 void QQuickTextDocumentWithImageResources::clear()
146 QTextDocument::clear();
150 QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
151 QTextDocument *, int, const QTextFormat &format)
153 if (format.isImageFormat()) {
154 QTextImageFormat imageFormat = format.toImageFormat();
156 const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
157 const int width = qRound(imageFormat.width());
158 const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
159 const int height = qRound(imageFormat.height());
161 QSizeF size(width, height);
162 if (!hasWidth || !hasHeight) {
163 QDeclarativeContext *context = qmlContext(parent());
164 QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
166 QDeclarativePixmap *p = loadPixmap(context, url);
174 QSize implicitSize = p->implicitSize();
178 size.setWidth(implicitSize.width());
180 size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
184 size.setHeight(implicitSize.height());
186 size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
194 void QQuickTextDocumentWithImageResources::drawObject(
195 QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
199 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
201 QDeclarativeContext *context = qmlContext(parent());
202 QUrl url = m_baseUrl.resolved(QUrl(format.name()));
204 QDeclarativePixmap *p = loadPixmap(context, url);
208 void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
213 markContentsDirty(0, characterCount());
217 QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
218 QDeclarativeContext *context, const QUrl &url)
221 QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
223 if (iter == m_resources.end()) {
224 QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
225 iter = m_resources.insert(url, p);
227 if (p->isLoading()) {
228 p->connectFinished(this, SLOT(requestFinished()));
233 QDeclarativePixmap *p = *iter;
235 if (!errors.contains(url)) {
237 qmlInfo(parent()) << p->error();
243 void QQuickTextDocumentWithImageResources::clearResources()
245 foreach (QDeclarativePixmap *pixmap, m_resources)
247 qDeleteAll(m_resources);
252 void QQuickTextDocumentWithImageResources::setText(const QString &text)
256 #ifndef QT_NO_TEXTHTMLPARSER
263 QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
265 QQuickTextPrivate::~QQuickTextPrivate()
268 delete textLine; textLine = 0;
274 qreal QQuickTextPrivate::getImplicitWidth() const
276 if (!requireImplicitWidth) {
277 // We don't calculate implicitWidth unless it is required.
278 // We need to force a size update now to ensure implicitWidth is calculated
279 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
280 me->requireImplicitWidth = true;
283 return implicitWidth;
286 void QQuickText::q_imagesLoaded()
292 void QQuickTextPrivate::updateLayout()
295 if (!q->isComponentComplete()) {
296 updateOnComponentComplete = true;
299 updateOnComponentComplete = false;
300 layoutTextElided = false;
302 if (!visibleImgTags.isEmpty())
303 visibleImgTags.clear();
304 needToUpdateLayout = false;
306 // Setup instance of QTextLayout for all cases other than richtext
308 if (textHasChanged) {
309 if (styledText && !text.isEmpty()) {
310 QDeclarativeStyledText::parse(text, layout, imgTags, qmlContext(q), !maximumLineCountValid);
312 layout.clearAdditionalFormats();
314 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
317 textHasChanged = false;
321 QTextBlockFormat::LineHeightTypes type;
322 type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
323 QTextBlockFormat blockFormat;
324 blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type);
325 for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
326 QTextCursor cursor(it);
327 cursor.mergeBlockFormat(blockFormat);
333 if (needToUpdateLayout) {
334 needToUpdateLayout = false;
335 textHasChanged = true;
340 void QQuickText::imageDownloadFinished()
344 (d->nbActiveDownloads)--;
346 // when all the remote images have been downloaded,
347 // if one of the sizes was not specified at parsing time
348 // we use the implicit size from pixmapcache and re-layout.
350 if (d->nbActiveDownloads == 0) {
351 bool needToUpdateLayout = false;
352 foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
353 if (!img->size.isValid()) {
354 img->size = img->pix->implicitSize();
355 needToUpdateLayout = true;
359 if (needToUpdateLayout) {
360 d->textHasChanged = true;
363 d->updateType = QQuickTextPrivate::UpdatePaintNode;
369 void QQuickTextPrivate::updateSize()
373 if (!q->isComponentComplete()) {
374 updateOnComponentComplete = true;
378 if (!requireImplicitWidth) {
379 emit q->implicitWidthChanged();
380 // if the implicitWidth is used, then updateSize() has already been called (recursively)
381 if (requireImplicitWidth)
385 invalidateImageCache();
387 QFontMetrics fm(font);
388 if (text.isEmpty()) {
389 qreal fontHeight = fm.height();
390 q->setImplicitSize(0, fontHeight);
391 paintedSize = QSize(0, fontHeight);
392 emit q->paintedSizeChanged();
393 updateType = UpdatePaintNode;
398 int dy = q->height();
401 #if defined(Q_OS_MAC)
402 layoutThread = QThread::currentThread();
405 //setup instance of QTextLayout for all cases other than richtext
407 QRect textRect = setupTextLayout();
408 layedOutTextRect = textRect;
409 size = textRect.size();
412 singleline = false; // richtext can't elide or be optimized for single-line case
414 doc->setDefaultFont(font);
415 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
416 if (rightToLeftText) {
417 if (horizontalAlignment == QQuickText::AlignLeft)
418 horizontalAlignment = QQuickText::AlignRight;
419 else if (horizontalAlignment == QQuickText::AlignRight)
420 horizontalAlignment = QQuickText::AlignLeft;
423 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
424 option.setWrapMode(QTextOption::WrapMode(wrapMode));
425 if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
426 option.setUseDesignMetrics(true);
427 doc->setDefaultTextOption(option);
428 if (requireImplicitWidth && q->widthValid()) {
429 doc->setTextWidth(-1);
430 naturalWidth = doc->idealWidth();
432 if (wrapMode != QQuickText::NoWrap && q->widthValid())
433 doc->setTextWidth(q->width());
435 doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
436 dy -= (int)doc->size().height();
437 QSize dsize = doc->size().toSize();
438 layedOutTextRect = QRect(QPoint(0,0), dsize);
439 size = QSize(int(doc->idealWidth()),dsize.height());
443 if (q->heightValid()) {
444 if (vAlign == QQuickText::AlignBottom)
446 else if (vAlign == QQuickText::AlignVCenter)
449 q->setBaselineOffset(fm.ascent() + yoff);
451 //### need to comfirm cost of always setting these for richText
452 internalWidthUpdate = true;
454 if (!q->widthValid())
455 iWidth = size.width();
456 else if (requireImplicitWidth)
457 iWidth = naturalWidth;
459 q->setImplicitSize(iWidth, size.height());
460 internalWidthUpdate = false;
463 q->setImplicitHeight(size.height());
464 if (paintedSize != size) {
466 emit q->paintedSizeChanged();
468 updateType = UpdatePaintNode;
472 QQuickTextLine::QQuickTextLine()
473 : QObject(), m_line(0), m_height(0)
477 void QQuickTextLine::setLine(QTextLine *line)
482 void QQuickTextLine::setLineOffset(int offset)
484 m_lineOffset = offset;
487 int QQuickTextLine::number() const
490 return m_line->lineNumber() + m_lineOffset;
494 qreal QQuickTextLine::width() const
497 return m_line->width();
501 void QQuickTextLine::setWidth(qreal width)
504 m_line->setLineWidth(width);
507 qreal QQuickTextLine::height() const
512 return m_line->height();
516 void QQuickTextLine::setHeight(qreal height)
519 m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
523 qreal QQuickTextLine::x() const
530 void QQuickTextLine::setX(qreal x)
533 m_line->setPosition(QPointF(x, m_line->y()));
536 qreal QQuickTextLine::y() const
543 void QQuickTextLine::setY(qreal y)
546 m_line->setPosition(QPointF(m_line->x(), y));
549 void QQuickText::doLayout()
555 bool QQuickTextPrivate::isLineLaidOutConnected()
557 static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
558 return this->isSignalConnected(idx);
561 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
565 #if defined(Q_OS_MAC)
566 if (QThread::currentThread() != paintingThread) {
567 if (!line.lineNumber())
572 textLine = new QQuickTextLine;
573 textLine->setLine(&line);
574 textLine->setY(height);
575 textLine->setHeight(0);
576 textLine->setLineOffset(lineOffset);
578 // use the text item's width by default if it has one and wrap is on
579 if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
580 textLine->setWidth(q->width());
582 textLine->setWidth(INT_MAX);
583 if (lineHeight != 1.0)
584 textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight);
586 emit q->lineLaidOut(textLine);
588 height += textLine->height();
590 #if defined(Q_OS_MAC)
591 linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height());
594 if (line.lineNumber() < linesRects.count()) {
595 QRectF r = linesRects.at(line.lineNumber());
596 line.setLineWidth(r.width());
597 line.setPosition(r.topLeft());
604 Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
606 Returns the size of the final text. This can be used to position the text vertically (the text is
607 already absolutely positioned horizontally).
610 QRect QQuickTextPrivate::setupTextLayout()
613 layout.setCacheEnabled(true);
615 QTextOption textOption = layout.textOption();
616 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
617 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
618 if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
619 textOption.setUseDesignMetrics(true);
620 layout.setTextOption(textOption);
621 layout.setFont(font);
623 if ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
624 || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight)) {
625 // we are elided and we have a zero width or height
628 emit q->truncatedChanged();
632 emit q->lineCountChanged();
635 if (requireImplicitWidth) {
636 // Layout to determine the implicit width.
637 layout.beginLayout();
639 for (int i = 0; i < maximumLineCount; ++i) {
640 QTextLine line = layout.createLine();
645 naturalWidth = layout.maximumWidth();
646 layout.clearLayout();
649 QFontMetrics fm(font);
650 qreal height = (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : fm.height() * lineHeight;
651 return QRect(0, 0, 0, height);
654 const int lineWidth = q->widthValid() ? q->width() : INT_MAX;
655 const bool customLayout = isLineLaidOutConnected();
656 const bool wasTruncated = truncated;
658 const bool singlelineElide = !styledText && elideMode != QQuickText::ElideNone && q->widthValid();
659 const bool multilineElide = !styledText
660 && elideMode == QQuickText::ElideRight
662 && (q->heightValid() || maximumLineCountValid);
663 const bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
665 const bool horizontalFit = fontSizeMode & QQuickText::HorizontalFit && q->widthValid();
666 const bool verticalFit = fontSizeMode & QQuickText::VerticalFit
667 && (q->heightValid() || (maximumLineCountValid && canWrap));
668 const bool pixelSize = font.pixelSize() != -1;
669 const QString layoutText = layout.text();
671 int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
672 int smallFont = fontSizeMode != QQuickText::FixedSize
673 ? qMin(pixelSize ? minimumPixelSize : minimumPointSize, largeFont)
675 int scaledFontSize = largeFont;
679 QFont scaledFont = font;
682 int visibleCount = 0;
693 scaledFont.setPixelSize(scaledFontSize);
695 scaledFont.setPointSize(scaledFontSize);
696 layout.setFont(scaledFont);
698 layout.beginLayout();
700 bool wrapped = false;
701 bool truncateHeight = false;
704 int characterCount = 0;
705 int unwrappedLineCount = 1;
708 line = layout.createLine();
709 for (visibleCount = 1; ; ++visibleCount) {
710 qreal preLayoutHeight = height;
713 setupCustomLineGeometry(line, height);
715 setLineGeometry(line, lineWidth, height);
718 if (multilineElide && height > q->height() && visibleCount > 1) {
720 truncateHeight = true;
721 height = preLayoutHeight;
724 characterCount = line.textStart() + line.textLength();
726 QTextLine previousLine = layout.lineAt(visibleCount - 2);
727 elideText = layoutText.mid(previousLine.textStart(), previousLine.textLength());
728 if (layoutText.at(line.textStart() - 1) != QChar::LineSeparator) {
729 line.setLineWidth(INT_MAX);
730 elideText += layoutText.mid(line.textStart(), line.textLength());
732 elideText[elideText.length() - 1] = elideChar;
734 line.setLineWidth(0);
735 line.setPosition(QPointF(FLT_MAX, FLT_MAX));
738 height -= (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : previousLine.height() * lineHeight;
742 QTextLine nextLine = layout.createLine();
743 if (!nextLine.isValid()) {
744 characterCount = line.textStart() + line.textLength();
745 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
747 height = preLayoutHeight;
749 elideText = layoutText.mid(line.textStart(), line.textLength());
751 br = br.united(line.naturalTextRect());
755 const bool wrappedLine = layoutText.at(nextLine.textStart() - 1) != QChar::LineSeparator;
756 wrapped |= wrappedLine;
759 ++unwrappedLineCount;
761 if (visibleCount == maximumLineCount) {
763 characterCount = nextLine.textStart() + nextLine.textLength();
765 if (multilineElide) {
766 height = preLayoutHeight;
768 elideText = layoutText.mid(line.textStart(), line.textLength());
770 nextLine.setLineWidth(INT_MAX);
771 elideText += layoutText.mid(nextLine.textStart(), nextLine.textLength());
773 elideText[elideText.length() - 1] = elideChar;
775 elideText += elideChar;
777 br = br.united(line.naturalTextRect());
779 nextLine.setLineWidth(0);
780 nextLine.setPosition(QPointF(FLT_MAX, FLT_MAX));
784 br = br.united(line.naturalTextRect());
791 naturalWidth = layout.maximumWidth();
794 if (requireImplicitWidth
795 && characterCount < layoutText.length()
796 && unwrappedLineCount < maximumLineCount) {
797 // Use a new layout to get the maximum width for the remaining text. Using a
798 // different layout excludes the truncated text from rendering.
799 QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
800 widthLayout.setTextOption(layout.textOption());
802 for (; unwrappedLineCount <= maximumLineCount; ++unwrappedLineCount) {
803 QTextLine line = widthLayout.createLine();
807 naturalWidth = qMax(naturalWidth, widthLayout.maximumWidth());
811 QRectF unelidedRect = br.united(line.naturalTextRect());
814 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
815 largeFont = scaledFontSize - 1;
816 scaledFontSize = (smallFont + largeFont) / 2;
817 if (smallFont > largeFont)
820 } else if (!verticalFit) {
821 smallFont = scaledFontSize;
822 scaledFontSize = (smallFont + largeFont + 1) / 2;
823 if (smallFont == largeFont)
829 if (truncateHeight || (q->heightValid() && unelidedRect.height() > q->height())) {
830 largeFont = scaledFontSize - 1;
831 scaledFontSize = (smallFont + largeFont + 1) / 2;
832 if (smallFont > largeFont)
835 smallFont = scaledFontSize;
836 scaledFontSize = (smallFont + largeFont + 1) / 2;
837 if (smallFont == largeFont)
841 } while (horizontalFit || verticalFit);
845 elideLayout = new QTextLayout;
846 elideLayout->setFont(layout.font());
847 elideLayout->setTextOption(layout.textOption());
848 elideLayout->setText(elideText);
849 elideLayout->setText(elideLayout->engine()->elidedText(Qt::TextElideMode(elideMode), lineWidth));
850 elideLayout->beginLayout();
852 QTextLine elidedLine = elideLayout->createLine();
853 elidedLine.setPosition(QPointF(0, height));
855 setupCustomLineGeometry(elidedLine, height, line.lineNumber());
857 setLineGeometry(elidedLine, lineWidth, height);
859 elideLayout->endLayout();
861 br = br.united(elidedLine.naturalTextRect());
863 if (visibleCount > 1)
864 line.setPosition(QPointF(FLT_MAX, FLT_MAX));
866 layout.clearLayout();
873 br.setHeight(height);
875 //Update the number of visible lines
876 if (lineCount != visibleCount) {
877 lineCount = visibleCount;
878 emit q->lineCountChanged();
881 if (truncated != wasTruncated)
882 emit q->truncatedChanged();
884 return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
887 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
890 line.setLineWidth(lineWidth);
892 if (imgTags.isEmpty()) {
893 line.setPosition(QPointF(line.position().x(), height));
894 height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight;
899 qreal textHeight = line.height();
900 qreal totalLineHeight = textHeight;
902 QList<QDeclarativeStyledTextImgTag *> imagesInLine;
904 foreach (QDeclarativeStyledTextImgTag *image, imgTags) {
905 if (image->position >= line.textStart() &&
906 image->position < line.textStart() + line.textLength()) {
909 QUrl url = qmlContext(q)->resolvedUrl(image->url);
910 image->pix = new QDeclarativePixmap(qmlEngine(q), url, image->size);
911 if (image->pix->isLoading()) {
912 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
914 } else if (image->pix->isReady()) {
915 if (!image->size.isValid()) {
916 image->size = image->pix->implicitSize();
917 // if the size of the image was not explicitly set, we need to
918 // call updateLayout() once again.
919 needToUpdateLayout = true;
921 } else if (image->pix->isError()) {
922 qmlInfo(q) << image->pix->error();
926 qreal ih = qreal(image->size.height());
927 if (image->align == QDeclarativeStyledTextImgTag::Top)
929 else if (image->align == QDeclarativeStyledTextImgTag::Middle)
930 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
932 image->pos.setY(textHeight - ih);
933 imagesInLine << image;
934 textTop = qMax(textTop, qAbs(image->pos.y()));
938 foreach (QDeclarativeStyledTextImgTag *image, imagesInLine) {
939 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
940 image->pos.setX(line.cursorToX(image->position));
941 image->pos.setY(image->pos.y() + height + textTop);
942 visibleImgTags << image;
945 line.setPosition(QPointF(line.position().x(), height + textTop));
946 height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : totalLineHeight * lineHeight;
950 Returns a painted version of the QQuickTextPrivate::layout QTextLayout.
951 If \a drawStyle is true, the style color overrides all colors in the document.
953 QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle)
955 QSize size = layedOutTextRect.size();
959 if (!size.isEmpty()) {
960 img.fill(Qt::transparent);
961 /*#ifdef Q_OS_MAC // Fails on CocoaX64
962 bool oldSmooth = qt_applefontsmoothing_enabled;
963 qt_applefontsmoothing_enabled = false;
966 /*#ifdef Q_OS_MAC // Fails on CocoaX64
967 qt_applefontsmoothing_enabled = oldSmooth;
969 drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
975 Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos. If
976 \a drawStyle is true, the style color overrides all colors in the document.
978 void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
981 painter->setPen(styleColor);
983 painter->setPen(color);
984 painter->setFont(font);
985 layout.draw(painter, pos);
986 if (!elidePos.isNull())
987 painter->drawText(pos + elidePos, elideChar);
991 Returns a painted version of the QQuickTextPrivate::doc QTextDocument.
992 If \a drawStyle is true, the style color overrides all colors in the document.
994 QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle)
996 QSize size = doc->size().toSize();
1000 img.fill(Qt::transparent);
1001 /*#ifdef Q_OS_MAC // Fails on CocoaX64
1002 bool oldSmooth = qt_applefontsmoothing_enabled;
1003 qt_applefontsmoothing_enabled = false;
1006 /*#ifdef Q_OS_MAC // Fails on CocoaX64
1007 qt_applefontsmoothing_enabled = oldSmooth;
1010 QAbstractTextDocumentLayout::PaintContext context;
1012 QTextOption oldOption(doc->defaultTextOption());
1014 context.palette.setColor(QPalette::Text, styleColor);
1015 QTextOption colorOption(doc->defaultTextOption());
1016 colorOption.setFlags(QTextOption::SuppressColors);
1017 doc->setDefaultTextOption(colorOption);
1019 context.palette.setColor(QPalette::Text, color);
1021 doc->documentLayout()->draw(&p, context);
1023 doc->setDefaultTextOption(oldOption);
1028 void QQuickTextPrivate::markDirty()
1031 if (!invalidateImageCache() && q->isComponentComplete()) {
1032 updateType = UpdatePaintNode;
1038 Mark the image cache as dirty.
1040 bool QQuickTextPrivate::invalidateImageCache()
1044 if (richTextAsImage || cacheAllTextAsImage || (disableDistanceField && style != QQuickText::Normal)) { // If actually using the image cache
1045 if (imageCacheDirty)
1048 imageCacheDirty = true;
1050 if (q->isComponentComplete())
1051 QCoreApplication::postEvent(q, new QEvent(QEvent::User));
1059 Tests if the image cache is dirty, and repaints it if it is.
1061 void QQuickTextPrivate::checkImageCache()
1065 if (!imageCacheDirty)
1068 if (text.isEmpty()) {
1076 QPixmap styledImage;
1079 textImage = textDocumentImage(false);
1080 if (style != QQuickText::Normal)
1081 styledImage = textDocumentImage(true); //### should use styleColor
1083 textImage = textLayoutImage(false);
1084 if (style != QQuickText::Normal)
1085 styledImage = textLayoutImage(true); //### should use styleColor
1090 case QQuickText::Outline:
1091 imageCache = new QPixmap(drawOutline(textImage, styledImage));
1093 case QQuickText::Sunken:
1094 imageCache = new QPixmap(drawOutline(textImage, styledImage, -1));
1096 case QQuickText::Raised:
1097 imageCache = new QPixmap(drawOutline(textImage, styledImage, 1));
1100 imageCache = new QPixmap(textImage);
1106 imageCacheDirty = false;
1107 textureImageCacheDirty = true;
1108 updateType = UpdatePaintNode;
1113 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1115 void QQuickTextPrivate::ensureDoc()
1119 doc = new QQuickTextDocumentWithImageResources(q);
1120 doc->setDocumentMargin(0);
1121 doc->setBaseUrl(q->baseUrl());
1122 FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
1127 Draw \a styleSource as an outline around \a source and return the new image.
1129 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
1131 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
1132 img.fill(Qt::transparent);
1137 pos += QPoint(-1, 0);
1138 ppm.drawPixmap(pos, styleSource);
1139 pos += QPoint(2, 0);
1140 ppm.drawPixmap(pos, styleSource);
1141 pos += QPoint(-1, -1);
1142 ppm.drawPixmap(pos, styleSource);
1143 pos += QPoint(0, 2);
1144 ppm.drawPixmap(pos, styleSource);
1146 pos += QPoint(0, -1);
1147 ppm.drawPixmap(pos, source);
1154 Draw \a styleSource below \a source at \a yOffset and return the new image.
1156 QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
1158 QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
1159 img.fill(Qt::transparent);
1163 ppm.drawPixmap(QPoint(0, yOffset), styleSource);
1164 ppm.drawPixmap(0, 0, source);
1172 \qmlclass Text QQuickText
1173 \inqmlmodule QtQuick 2
1174 \ingroup qml-basic-visual-elements
1175 \brief The Text item allows you to add formatted text to a scene.
1178 Text items can display both plain and rich text. For example, red text with
1179 a specific font and size can be defined like this:
1183 text: "Hello World!"
1184 font.family: "Helvetica"
1190 Rich text is defined using HTML-style markup:
1194 text: "<b>Hello</b> <i>World!</i>"
1198 \image declarative-text.png
1200 If height and width are not explicitly set, Text will attempt to determine how
1201 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1202 prefer width to height (all text will be placed on a single line).
1204 The \l elide property can alternatively be used to fit a single line of
1205 plain text to a set width.
1207 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1208 HTML img tags that load remote images, the text is reloaded.
1210 Text provides read-only text. For editable text, see \l TextEdit.
1212 \sa {declarative/text/fonts}{Fonts example}
1214 QQuickText::QQuickText(QQuickItem *parent)
1215 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1221 QQuickText::~QQuickText()
1226 \qmlproperty bool QtQuick2::Text::clip
1227 This property holds whether the text is clipped.
1229 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1231 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1235 \qmlproperty bool QtQuick2::Text::smooth
1237 This property holds whether the text is smoothly scaled or transformed.
1239 Smooth filtering gives better visual quality, but is slower. If
1240 the item is displayed at its natural size, this property has no visual or
1243 \note Generally scaling artifacts are only visible if the item is stationary on
1244 the screen. A common pattern when animating an item is to disable smooth
1245 filtering at the beginning of the animation and reenable it at the conclusion.
1249 \qmlsignal QtQuick2::Text::onLineLaidOut(line)
1251 This handler is called for every line during the layout process.
1252 This gives the opportunity to position and resize a line as it is being laid out.
1253 It can for example be used to create columns or lay out text around objects.
1255 The properties of a line are:
1257 \o number (read-only)
1264 For example, this will move the first 5 lines of a text element by 100 pixels to the right:
1267 if (line.number < 5) {
1268 line.x = line.x + 100
1269 line.width = line.width - 100
1276 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1278 This handler is called when the user clicks on a link embedded in the text.
1279 The link must be in rich text or HTML format and the
1280 \a link string provides access to the particular link.
1282 \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
1284 The example code will display the text
1285 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1287 Clicking on the highlighted link will output
1288 \tt{http://qt.nokia.com link activated} to the console.
1292 \qmlproperty string QtQuick2::Text::font.family
1294 Sets the family name of the font.
1296 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1297 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1298 If the family isn't available a family will be set using the font matching algorithm.
1302 \qmlproperty bool QtQuick2::Text::font.bold
1304 Sets whether the font weight is bold.
1308 \qmlproperty enumeration QtQuick2::Text::font.weight
1310 Sets the font's weight.
1312 The weight can be one of:
1315 \o Font.Normal - the default
1322 Text { text: "Hello"; font.weight: Font.DemiBold }
1327 \qmlproperty bool QtQuick2::Text::font.italic
1329 Sets whether the font has an italic style.
1333 \qmlproperty bool QtQuick2::Text::font.underline
1335 Sets whether the text is underlined.
1339 \qmlproperty bool QtQuick2::Text::font.strikeout
1341 Sets whether the font has a strikeout style.
1345 \qmlproperty real QtQuick2::Text::font.pointSize
1347 Sets the font size in points. The point size must be greater than zero.
1351 \qmlproperty int QtQuick2::Text::font.pixelSize
1353 Sets the font size in pixels.
1355 Using this function makes the font device dependent.
1356 Use \c pointSize to set the size of the font in a device independent manner.
1360 \qmlproperty real QtQuick2::Text::font.letterSpacing
1362 Sets the letter spacing for the font.
1364 Letter spacing changes the default spacing between individual letters in the font.
1365 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1369 \qmlproperty real QtQuick2::Text::font.wordSpacing
1371 Sets the word spacing for the font.
1373 Word spacing changes the default spacing between individual words.
1374 A positive value increases the word spacing by a corresponding amount of pixels,
1375 while a negative value decreases the inter-word spacing accordingly.
1379 \qmlproperty enumeration QtQuick2::Text::font.capitalization
1381 Sets the capitalization for the text.
1384 \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1385 \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1386 \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1387 \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
1388 \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1392 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1395 QFont QQuickText::font() const
1397 Q_D(const QQuickText);
1398 return d->sourceFont;
1401 void QQuickText::setFont(const QFont &font)
1404 if (d->sourceFont == font)
1407 d->sourceFont = font;
1408 QFont oldFont = d->font;
1411 if (d->font.pointSizeF() != -1) {
1413 qreal size = qRound(d->font.pointSizeF()*2.0);
1414 d->font.setPointSizeF(size/2.0);
1417 if (oldFont != d->font)
1420 emit fontChanged(d->sourceFont);
1424 \qmlproperty string QtQuick2::Text::text
1426 The text to display. Text supports both plain and rich text strings.
1428 The item will try to automatically determine whether the text should
1429 be treated as styled text. This determination is made using Qt::mightBeRichText().
1431 QString QQuickText::text() const
1433 Q_D(const QQuickText);
1437 void QQuickText::setText(const QString &n)
1443 d->richText = d->format == RichText;
1444 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1446 if (isComponentComplete()) {
1450 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1451 d->richTextAsImage = enableImageCache();
1453 d->rightToLeftText = d->text.isRightToLeft();
1455 d->determineHorizontalAlignment();
1457 d->textHasChanged = true;
1458 qDeleteAll(d->imgTags);
1461 emit textChanged(d->text);
1465 \qmlproperty color QtQuick2::Text::color
1469 An example of green text defined using hexadecimal notation:
1477 An example of steel blue text defined using an SVG color name:
1485 QColor QQuickText::color() const
1487 Q_D(const QQuickText);
1491 void QQuickText::setColor(const QColor &color)
1494 if (d->color == color)
1499 emit colorChanged(d->color);
1502 \qmlproperty enumeration QtQuick2::Text::style
1504 Set an additional text style.
1506 Supported text styles are:
1508 \o Text.Normal - the default
1516 Text { font.pointSize: 24; text: "Normal" }
1517 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1518 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1519 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1523 \image declarative-textstyle.png
1525 QQuickText::TextStyle QQuickText::style() const
1527 Q_D(const QQuickText);
1531 void QQuickText::setStyle(QQuickText::TextStyle style)
1534 if (d->style == style)
1537 // changing to/from Normal requires the boundingRect() to change
1538 if (isComponentComplete() && (d->style == Normal || style == Normal)) {
1539 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1544 emit styleChanged(d->style);
1548 \qmlproperty color QtQuick2::Text::styleColor
1550 Defines the secondary color used by text styles.
1552 \c styleColor is used as the outline color for outlined text, and as the
1553 shadow color for raised or sunken text. If no style has been set, it is not
1557 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1562 QColor QQuickText::styleColor() const
1564 Q_D(const QQuickText);
1565 return d->styleColor;
1568 void QQuickText::setStyleColor(const QColor &color)
1571 if (d->styleColor == color)
1574 d->styleColor = color;
1576 emit styleColorChanged(d->styleColor);
1580 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1581 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1582 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1584 Sets the horizontal and vertical alignment of the text within the Text items
1585 width and height. By default, the text is vertically aligned to the top. Horizontal
1586 alignment follows the natural alignment of the text, for example text that is read
1587 from left to right will be aligned to the left.
1589 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1590 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1591 and \c Text.AlignVCenter.
1593 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1594 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1595 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1598 When using the attached property LayoutMirroring::enabled to mirror application
1599 layouts, the horizontal alignment of text will also be mirrored. However, the property
1600 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1601 of Text, use the read-only property \c effectiveHorizontalAlignment.
1603 QQuickText::HAlignment QQuickText::hAlign() const
1605 Q_D(const QQuickText);
1609 void QQuickText::setHAlign(HAlignment align)
1612 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1613 d->hAlignImplicit = false;
1614 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1618 void QQuickText::resetHAlign()
1621 d->hAlignImplicit = true;
1622 if (isComponentComplete() && d->determineHorizontalAlignment())
1626 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1628 Q_D(const QQuickText);
1629 QQuickText::HAlignment effectiveAlignment = d->hAlign;
1630 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1631 switch (d->hAlign) {
1632 case QQuickText::AlignLeft:
1633 effectiveAlignment = QQuickText::AlignRight;
1635 case QQuickText::AlignRight:
1636 effectiveAlignment = QQuickText::AlignLeft;
1642 return effectiveAlignment;
1645 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1648 if (hAlign != alignment || forceAlign) {
1649 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1652 emit q->horizontalAlignmentChanged(hAlign);
1653 if (oldEffectiveHAlign != q->effectiveHAlign())
1654 emit q->effectiveHorizontalAlignmentChanged();
1660 bool QQuickTextPrivate::determineHorizontalAlignment()
1662 if (hAlignImplicit) {
1663 bool alignToRight = text.isEmpty() ? qApp->inputPanel()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1664 return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1669 void QQuickTextPrivate::mirrorChange()
1672 if (q->isComponentComplete()) {
1673 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1675 emit q->effectiveHorizontalAlignmentChanged();
1680 QTextDocument *QQuickTextPrivate::textDocument()
1685 QQuickText::VAlignment QQuickText::vAlign() const
1687 Q_D(const QQuickText);
1691 void QQuickText::setVAlign(VAlignment align)
1694 if (d->vAlign == align)
1698 emit verticalAlignmentChanged(align);
1702 \qmlproperty enumeration QtQuick2::Text::wrapMode
1704 Set this property to wrap the text to the Text item's width. The text will only
1705 wrap if an explicit width has been set. wrapMode can be one of:
1708 \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
1709 \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
1710 \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1711 \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.
1714 QQuickText::WrapMode QQuickText::wrapMode() const
1716 Q_D(const QQuickText);
1720 void QQuickText::setWrapMode(WrapMode mode)
1723 if (mode == d->wrapMode)
1729 emit wrapModeChanged();
1733 \qmlproperty int QtQuick2::Text::lineCount
1735 Returns the number of lines visible in the text item.
1737 This property is not supported for rich text.
1739 \sa maximumLineCount
1741 int QQuickText::lineCount() const
1743 Q_D(const QQuickText);
1744 return d->lineCount;
1748 \qmlproperty bool QtQuick2::Text::truncated
1750 Returns true if the text has been truncated due to \l maximumLineCount
1753 This property is not supported for rich text.
1755 \sa maximumLineCount, elide
1757 bool QQuickText::truncated() const
1759 Q_D(const QQuickText);
1760 return d->truncated;
1764 \qmlproperty int QtQuick2::Text::maximumLineCount
1766 Set this property to limit the number of lines that the text item will show.
1767 If elide is set to Text.ElideRight, the text will be elided appropriately.
1768 By default, this is the value of the largest possible integer.
1770 This property is not supported for rich text.
1772 \sa lineCount, elide
1774 int QQuickText::maximumLineCount() const
1776 Q_D(const QQuickText);
1777 return d->maximumLineCount;
1780 void QQuickText::setMaximumLineCount(int lines)
1784 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1785 if (d->maximumLineCount != lines) {
1786 d->maximumLineCount = lines;
1788 emit maximumLineCountChanged();
1792 void QQuickText::resetMaximumLineCount()
1795 setMaximumLineCount(INT_MAX);
1796 d->elidePos = QPointF();
1797 if (d->truncated != false) {
1798 d->truncated = false;
1799 emit truncatedChanged();
1804 \qmlproperty enumeration QtQuick2::Text::textFormat
1806 The way the text property should be displayed.
1808 Supported text formats are:
1811 \o Text.AutoText (default)
1817 If the text format is \c Text.AutoText the text element
1818 will automatically determine whether the text should be treated as
1819 styled text. This determination is made using Qt::mightBeRichText()
1820 which uses a fast and therefore simple heuristic. It mainly checks
1821 whether there is something that looks like a tag before the first
1822 line break. Although the result may be correct for common cases,
1823 there is no guarantee.
1825 Text.StyledText is an optimized format supporting some basic text
1826 styling markup, in the style of html 3.2:
1830 <strong></strong> - bold
1834 <u> - underlined text
1835 <font color="color_name" size="1-7"></font>
1836 <h1> to <h6> - headers
1837 <a href=""> - anchor
1838 <img src="" align="top,middle,bottom" width="" height=""> - inline images
1839 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1840 <pre></pre> - preformatted
1844 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1853 text: "<b>Hello</b> <i>World!</i>"
1857 textFormat: Text.RichText
1858 text: "<b>Hello</b> <i>World!</i>"
1862 textFormat: Text.PlainText
1863 text: "<b>Hello</b> <i>World!</i>"
1867 \o \image declarative-textformat.png
1870 QQuickText::TextFormat QQuickText::textFormat() const
1872 Q_D(const QQuickText);
1876 void QQuickText::setTextFormat(TextFormat format)
1879 if (format == d->format)
1882 bool wasRich = d->richText;
1883 d->richText = format == RichText;
1884 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1886 if (isComponentComplete()) {
1887 if (!wasRich && d->richText) {
1889 d->doc->setText(d->text);
1890 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
1891 d->richTextAsImage = enableImageCache();
1893 d->rightToLeftText = d->text.isRightToLeft();
1895 d->determineHorizontalAlignment();
1899 emit textFormatChanged(d->format);
1903 \qmlproperty enumeration QtQuick2::Text::elide
1905 Set this property to elide parts of the text fit to the Text item's width.
1906 The text will only elide if an explicit width has been set.
1908 This property cannot be used with rich text.
1912 \o Text.ElideNone - the default
1918 If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
1919 text. The text will only elide if \c maximumLineCount, or \c height has been set.
1920 If both \c maximumLineCount and \c height are set, \c maximumLineCount will
1921 apply unless the lines do not fit in the height allowed.
1923 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1924 the first string that fits will be used, otherwise the last will be elided.
1926 Multi-length strings are ordered from longest to shortest, separated by the
1927 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1929 QQuickText::TextElideMode QQuickText::elideMode() const
1931 Q_D(const QQuickText);
1932 return d->elideMode;
1935 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
1938 if (mode == d->elideMode)
1941 d->elideMode = mode;
1944 emit elideModeChanged(d->elideMode);
1948 \qmlproperty url QtQuick2::Text::baseUrl
1950 This property specifies a base URL which is used to resolve relative URLs
1953 By default is the url of the Text element.
1956 QUrl QQuickText::baseUrl() const
1958 Q_D(const QQuickText);
1959 if (d->baseUrl.isEmpty()) {
1960 if (QDeclarativeContext *context = qmlContext(this))
1961 const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
1966 void QQuickText::setBaseUrl(const QUrl &url)
1969 if (baseUrl() != url) {
1973 d->doc->setBaseUrl(url);
1974 emit baseUrlChanged();
1978 void QQuickText::resetBaseUrl()
1980 if (QDeclarativeContext *context = qmlContext(this))
1981 setBaseUrl(context->baseUrl());
1987 QRectF QQuickText::boundingRect() const
1989 Q_D(const QQuickText);
1991 QRect rect = d->layedOutTextRect;
1992 if (d->style != Normal)
1993 rect.adjust(-1, 0, 1, 2);
1995 // Could include font max left/right bearings to either side of rectangle.
1998 switch (d->vAlign) {
2002 rect.moveTop(h - rect.height());
2005 rect.moveTop((h - rect.height()) / 2);
2009 return QRectF(rect);
2013 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2016 if (d->text.isEmpty()) {
2017 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2021 bool widthChanged = newGeometry.width() != oldGeometry.width();
2022 bool heightChanged = newGeometry.height() != oldGeometry.height();
2023 bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
2024 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2025 bool elide = d->elideMode != QQuickText::ElideNone;
2026 bool scaleFont = d->fontSizeMode != QQuickText::FixedSize && (widthValid() || heightValid());
2028 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2029 goto geomChangeDone;
2031 if (leftAligned && !wrapped && !elide && !scaleFont)
2032 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2034 if (!widthChanged && !wrapped && d->singleline && !scaleFont)
2035 goto geomChangeDone; // only height has changed which doesn't affect single line unwrapped text
2037 if (!widthChanged && wrapped && d->elideMode != QQuickText::ElideRight && !scaleFont)
2038 goto geomChangeDone; // only height changed and no multiline eliding.
2040 if (leftAligned && d->elideMode == QQuickText::ElideRight && !d->truncated && d->singleline
2041 && !wrapped && newGeometry.width() > oldGeometry.width() && !scaleFont)
2042 goto geomChangeDone; // Eliding not affected if we're not currently truncated and we get wider.
2044 if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
2046 goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
2047 if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount)
2048 goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
2051 if (d->updateOnComponentComplete) {
2052 // We need to re-elide
2055 // We just need to re-layout
2060 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2063 void QQuickText::triggerPreprocess()
2066 if (d->updateType == QQuickTextPrivate::UpdateNone)
2067 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2071 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2076 if (d->text.isEmpty()) {
2081 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2082 // Update done in preprocess() in the nodes
2083 d->updateType = QQuickTextPrivate::UpdateNone;
2087 d->updateType = QQuickTextPrivate::UpdateNone;
2089 QRectF bounds = boundingRect();
2091 // We need to make sure the layout is done in the current thread
2092 #if defined(Q_OS_MAC)
2093 d->paintingThread = QThread::currentThread();
2094 if (d->layoutThread != d->paintingThread)
2098 // XXX todo - some styled text can be done by the QQuickTextNode
2099 if (d->richTextAsImage || d->cacheAllTextAsImage || (d->disableDistanceField && d->style != Normal)) {
2100 bool wasDirty = d->textureImageCacheDirty;
2101 d->textureImageCacheDirty = false;
2103 if (!d->imageCache || d->imageCache->isNull()) {
2108 QSGImageNode *node = 0;
2109 if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) {
2111 node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
2112 d->texture = new QSGPlainTexture();
2114 d->nodeType = QQuickTextPrivate::NodeIsTexture;
2116 node = static_cast<QSGImageNode *>(oldNode);
2117 Q_ASSERT(d->texture);
2121 qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache->toImage());
2122 node->setTexture(0);
2123 node->setTexture(d->texture);
2126 node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache->width(), d->imageCache->height()));
2127 node->setSourceRect(QRectF(0, 0, 1, 1));
2128 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
2129 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
2130 node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
2136 QQuickTextNode *node = 0;
2137 if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
2139 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2140 d->nodeType = QQuickTextPrivate::NodeIsText;
2142 node = static_cast<QQuickTextNode *>(oldNode);
2145 node->deleteContent();
2146 node->setMatrix(QMatrix4x4());
2150 node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor);
2152 } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
2153 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
2155 node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor);
2158 foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
2159 QDeclarativePixmap *pix = img->pix;
2160 if (pix && pix->isReady())
2161 node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
2167 bool QQuickText::event(QEvent *e)
2170 if (e->type() == QEvent::User) {
2171 d->checkImageCache();
2174 return QQuickImplicitSizeItem::event(e);
2178 void QQuickText::updatePolish()
2181 if (d->updateLayoutOnPolish)
2188 \qmlproperty real QtQuick2::Text::paintedWidth
2190 Returns the width of the text, including width past the width
2191 which is covered due to insufficient wrapping if WrapMode is set.
2193 qreal QQuickText::paintedWidth() const
2195 Q_D(const QQuickText);
2196 return d->paintedSize.width();
2200 \qmlproperty real QtQuick2::Text::paintedHeight
2202 Returns the height of the text, including height past the height
2203 which is covered due to there being more text than fits in the set height.
2205 qreal QQuickText::paintedHeight() const
2207 Q_D(const QQuickText);
2208 return d->paintedSize.height();
2212 \qmlproperty real QtQuick2::Text::lineHeight
2214 Sets the line height for the text.
2215 The value can be in pixels or a multiplier depending on lineHeightMode.
2217 The default value is a multiplier of 1.0.
2218 The line height must be a positive value.
2220 qreal QQuickText::lineHeight() const
2222 Q_D(const QQuickText);
2223 return d->lineHeight;
2226 void QQuickText::setLineHeight(qreal lineHeight)
2230 if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
2233 d->lineHeight = lineHeight;
2235 emit lineHeightChanged(lineHeight);
2239 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2241 This property determines how the line height is specified.
2242 The possible values are:
2245 \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
2246 line (as a multiplier). For example, set to 2 for double spacing.
2247 \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2250 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2252 Q_D(const QQuickText);
2253 return d->lineHeightMode;
2256 void QQuickText::setLineHeightMode(LineHeightMode mode)
2259 if (mode == d->lineHeightMode)
2262 d->lineHeightMode = mode;
2265 emit lineHeightModeChanged(mode);
2269 \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2271 This property specifies how the font size of the displayed text is determined.
2272 The possible values are:
2275 \o Text.FixedSize (default) - The size specified by \l font.pixelSize
2276 or \l font.pointSize is used.
2277 \o Text.HorizontalFit - The largest size up to the size specified that fits
2278 within the width of the item without wrapping is used.
2279 \o Text.VerticalFit - The largest size up to the size specified that fits
2280 the height of the item is used.
2281 \o Text.Fit - The largest size up to the size specified the fits within the
2282 width and height of the item is used.
2285 The font size of fitted text has a minimum bound specified by the
2286 minimumPointSize or minimumPixelSize property and maximum bound specified
2287 by either the \l font.pointSize or \l font.pixelSize properties.
2289 If the text does not fit within the item bounds with the minimum font size
2290 the text will be elided as per the \l elide property.
2293 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2295 Q_D(const QQuickText);
2296 return d->fontSizeMode;
2299 void QQuickText::setFontSizeMode(FontSizeMode mode)
2302 if (d->fontSizeMode == mode)
2307 d->fontSizeMode = mode;
2308 emit fontSizeModeChanged();
2312 \qmlproperty int QtQuick2::Text::minimumPixelSize
2314 This property specifies the minimum font pixel size of text scaled by the
2315 fontSizeMode property.
2317 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2318 property is ignored.
2321 int QQuickText::minimumPixelSize() const
2323 Q_D(const QQuickText);
2324 return d->minimumPixelSize;
2327 void QQuickText::setMinimumPixelSize(int size)
2330 if (d->minimumPixelSize == size)
2333 if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
2335 d->minimumPixelSize = size;
2336 emit minimumPixelSizeChanged();
2340 \qmlproperty int QtQuick2::Text::minimumPointSize
2342 This property specifies the minimum font point \l size of text scaled by
2343 the fontSizeMode property.
2345 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2346 property is ignored.
2349 int QQuickText::minimumPointSize() const
2351 Q_D(const QQuickText);
2352 return d->minimumPointSize;
2355 void QQuickText::setMinimumPointSize(int size)
2358 if (d->minimumPointSize == size)
2361 if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
2363 d->minimumPointSize = size;
2364 emit minimumPointSizeChanged();
2368 Returns the number of resources (images) that are being loaded asynchronously.
2370 int QQuickText::resourcesLoading() const
2372 Q_D(const QQuickText);
2373 return d->doc ? d->doc->resourcesLoading() : 0;
2377 void QQuickText::componentComplete()
2380 if (d->updateOnComponentComplete) {
2383 d->doc->setText(d->text);
2384 d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
2385 d->richTextAsImage = enableImageCache();
2387 d->rightToLeftText = d->text.isRightToLeft();
2389 d->determineHorizontalAlignment();
2391 QQuickItem::componentComplete();
2392 if (d->updateOnComponentComplete)
2395 // Enable accessibility for text items.
2396 d->setAccessibleFlagAndListener();
2400 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
2403 for (int i = 0; i < layout.lineCount(); ++i) {
2404 QTextLine line = layout.lineAt(i);
2405 if (line.naturalTextRect().contains(mousePos)) {
2406 int charPos = line.xToCursor(mousePos.x());
2407 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
2408 if (formatRange.format.isAnchor()
2409 && charPos >= formatRange.start
2410 && charPos <= formatRange.start + formatRange.length) {
2411 return formatRange.format.anchorHref();
2421 bool QQuickTextPrivate::isLinkActivatedConnected()
2423 static int idx = this->signalIndex("linkActivated(QString)");
2424 return this->isSignalConnected(idx);
2428 void QQuickText::mousePressEvent(QMouseEvent *event)
2432 if (d->isLinkActivatedConnected()) {
2434 d->activeLink = d->anchorAt(event->localPos());
2435 else if (d->richText && d->doc)
2436 d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
2439 if (d->activeLink.isEmpty())
2440 event->setAccepted(false);
2442 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2444 if (!event->isAccepted())
2445 QQuickItem::mousePressEvent(event);
2450 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2454 // ### confirm the link, and send a signal out
2457 if (d->isLinkActivatedConnected()) {
2459 link = d->anchorAt(event->localPos());
2460 else if (d->richText && d->doc)
2461 link = d->doc->documentLayout()->anchorAt(event->localPos());
2464 if (!link.isEmpty() && d->activeLink == link)
2465 emit linkActivated(d->activeLink);
2467 event->setAccepted(false);
2469 if (!event->isAccepted())
2470 QQuickItem::mouseReleaseEvent(event);