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 QtQml 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/qqmlglobal_p.h>
47 #include <private/qsgadaptationlayer_p.h>
48 #include "qquicktextnode_p.h"
49 #include "qquickimage_p_p.h"
50 #include <QtQuick/private/qsgtexture_p.h>
52 #include <QtQml/qqmlinfo.h>
53 #include <QtGui/qevent.h>
54 #include <QtGui/qabstracttextdocumentlayout.h>
55 #include <QtGui/qpainter.h>
56 #include <QtGui/qtextdocument.h>
57 #include <QtGui/qtextobject.h>
58 #include <QtGui/qtextcursor.h>
59 #include <QtGui/qguiapplication.h>
60 #include <QtGui/qinputmethod.h>
62 #include <private/qtextengine_p.h>
63 #include <private/qquickstyledtext_p.h>
64 #include <QtQuick/private/qquickpixmapcache_p.h>
72 const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
74 QQuickTextPrivate::QQuickTextPrivate()
75 : elideLayout(0), textLine(0)
77 , layoutThread(0), paintingThread(0)
79 , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
80 , lineCount(1), multilengthEos(-1)
81 , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
82 , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
83 , style(QQuickText::Normal)
84 , updateType(UpdatePaintNode)
85 , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
86 , styledText(false), singleline(false), internalWidthUpdate(false), requireImplicitWidth(false)
87 , truncated(false), hAlignImplicit(true), rightToLeftText(false)
88 , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
92 QQuickTextPrivate::ExtraData::ExtraData()
95 , minimumPixelSize(12)
96 , minimumPointSize(12)
97 , nbActiveDownloads(0)
98 , maximumLineCount(INT_MAX)
99 , lineHeightMode(QQuickText::ProportionalHeight)
100 , fontSizeMode(QQuickText::FixedSize)
104 void QQuickTextPrivate::init()
107 q->setAcceptedMouseButtons(Qt::LeftButton);
108 q->setFlag(QQuickItem::ItemHasContents);
111 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
112 : QTextDocument(parent), outstanding(0)
114 setUndoRedoEnabled(false);
115 documentLayout()->registerHandler(QTextFormat::ImageObject, this);
118 QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
120 if (!m_resources.isEmpty())
121 qDeleteAll(m_resources);
124 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
126 QQmlContext *context = qmlContext(parent());
127 QUrl url = m_baseUrl.resolved(name);
129 if (type == QTextDocument::ImageResource) {
130 QQuickPixmap *p = loadPixmap(context, url);
134 return QTextDocument::loadResource(type,url); // The *resolved* URL
137 void QQuickTextDocumentWithImageResources::requestFinished()
140 if (outstanding == 0) {
141 markContentsDirty(0, characterCount());
146 void QQuickTextDocumentWithImageResources::clear()
150 QTextDocument::clear();
154 QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
155 QTextDocument *, int, const QTextFormat &format)
157 if (format.isImageFormat()) {
158 QTextImageFormat imageFormat = format.toImageFormat();
160 const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
161 const int width = qRound(imageFormat.width());
162 const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
163 const int height = qRound(imageFormat.height());
165 QSizeF size(width, height);
166 if (!hasWidth || !hasHeight) {
167 QQmlContext *context = qmlContext(parent());
168 QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
170 QQuickPixmap *p = loadPixmap(context, url);
178 QSize implicitSize = p->implicitSize();
182 size.setWidth(implicitSize.width());
184 size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
188 size.setHeight(implicitSize.height());
190 size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
198 void QQuickTextDocumentWithImageResources::drawObject(
199 QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
203 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
205 QQmlContext *context = qmlContext(parent());
206 QUrl url = m_baseUrl.resolved(QUrl(format.name()));
208 QQuickPixmap *p = loadPixmap(context, url);
212 void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
217 markContentsDirty(0, characterCount());
221 QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
222 QQmlContext *context, const QUrl &url)
225 QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
227 if (iter == m_resources.end()) {
228 QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
229 iter = m_resources.insert(url, p);
231 if (p->isLoading()) {
232 p->connectFinished(this, SLOT(requestFinished()));
237 QQuickPixmap *p = *iter;
239 if (!errors.contains(url)) {
241 qmlInfo(parent()) << p->error();
247 void QQuickTextDocumentWithImageResources::clearResources()
249 foreach (QQuickPixmap *pixmap, m_resources)
251 qDeleteAll(m_resources);
256 void QQuickTextDocumentWithImageResources::setText(const QString &text)
260 #ifndef QT_NO_TEXTHTMLPARSER
267 QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
269 QQuickTextPrivate::~QQuickTextPrivate()
272 delete textLine; textLine = 0;
277 qreal QQuickTextPrivate::getImplicitWidth() const
279 if (!requireImplicitWidth) {
280 // We don't calculate implicitWidth unless it is required.
281 // We need to force a size update now to ensure implicitWidth is calculated
282 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
283 me->requireImplicitWidth = true;
286 return implicitWidth;
289 void QQuickText::q_imagesLoaded()
295 void QQuickTextPrivate::updateLayout()
298 if (!q->isComponentComplete()) {
299 updateOnComponentComplete = true;
302 updateOnComponentComplete = false;
303 layoutTextElided = false;
305 if (!visibleImgTags.isEmpty())
306 visibleImgTags.clear();
307 needToUpdateLayout = false;
309 // Setup instance of QTextLayout for all cases other than richtext
311 if (textHasChanged) {
312 if (styledText && !text.isEmpty()) {
313 layout.setFont(font);
314 // needs temporary bool because formatModifiesFontSize is in a bit-field
315 bool fontSizeModified = false;
316 QQuickStyledText::parse(text, layout, imgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
317 formatModifiesFontSize = fontSizeModified;
319 layout.clearAdditionalFormats();
321 multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
322 if (multilengthEos != -1) {
323 tmp = tmp.mid(0, multilengthEos);
324 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
325 } else if (tmp.contains(QLatin1Char('\n'))) {
326 // Replace always does a detach. Checking for the new line character first
327 // means iterating over those items again if found but prevents a realloc
329 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
333 textHasChanged = false;
337 QTextBlockFormat::LineHeightTypes type;
338 type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
339 QTextBlockFormat blockFormat;
340 blockFormat.setLineHeight((lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), type);
341 for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
342 QTextCursor cursor(it);
343 cursor.mergeBlockFormat(blockFormat);
349 if (needToUpdateLayout) {
350 needToUpdateLayout = false;
351 textHasChanged = true;
356 void QQuickText::imageDownloadFinished()
360 (d->extra->nbActiveDownloads)--;
362 // when all the remote images have been downloaded,
363 // if one of the sizes was not specified at parsing time
364 // we use the implicit size from pixmapcache and re-layout.
366 if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
367 bool needToUpdateLayout = false;
368 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
369 if (!img->size.isValid()) {
370 img->size = img->pix->implicitSize();
371 needToUpdateLayout = true;
375 if (needToUpdateLayout) {
376 d->textHasChanged = true;
379 d->updateType = QQuickTextPrivate::UpdatePaintNode;
385 void QQuickTextPrivate::updateSize()
389 if (!q->isComponentComplete()) {
390 updateOnComponentComplete = true;
394 if (!requireImplicitWidth) {
395 emit q->implicitWidthChanged();
396 // if the implicitWidth is used, then updateSize() has already been called (recursively)
397 if (requireImplicitWidth)
401 QFontMetricsF fm(font);
402 if (text.isEmpty()) {
403 qreal fontHeight = fm.height();
404 q->setImplicitSize(0, fontHeight);
405 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
406 emit q->contentSizeChanged();
407 updateType = UpdatePaintNode;
412 qreal naturalWidth = 0;
414 qreal dy = q->height();
416 QSizeF previousSize = layedOutTextRect.size();
417 #if defined(Q_OS_MAC)
418 layoutThread = QThread::currentThread();
421 //setup instance of QTextLayout for all cases other than richtext
423 QRectF textRect = setupTextLayout(&naturalWidth);
425 if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
426 return; // get this far we'll get a warning to that effect if it is.
428 layedOutTextRect = textRect;
429 size = textRect.size();
432 singleline = false; // richtext can't elide or be optimized for single-line case
434 extra->doc->setDefaultFont(font);
435 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
436 if (rightToLeftText) {
437 if (horizontalAlignment == QQuickText::AlignLeft)
438 horizontalAlignment = QQuickText::AlignRight;
439 else if (horizontalAlignment == QQuickText::AlignRight)
440 horizontalAlignment = QQuickText::AlignLeft;
443 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
444 option.setWrapMode(QTextOption::WrapMode(wrapMode));
445 option.setUseDesignMetrics(true);
446 extra->doc->setDefaultTextOption(option);
447 if (requireImplicitWidth && q->widthValid()) {
448 extra->doc->setTextWidth(-1);
449 naturalWidth = extra->doc->idealWidth();
450 const bool wasInLayout = internalWidthUpdate;
451 internalWidthUpdate = true;
452 q->setImplicitWidth(naturalWidth);
453 internalWidthUpdate = wasInLayout;
455 if (internalWidthUpdate)
457 if (wrapMode != QQuickText::NoWrap && q->widthValid())
458 extra->doc->setTextWidth(q->width());
460 extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
461 dy -= extra->doc->size().height();
462 QSizeF dsize = extra->doc->size();
463 layedOutTextRect = QRectF(QPointF(0,0), dsize);
464 size = QSizeF(extra->doc->idealWidth(),dsize.height());
468 if (q->heightValid()) {
469 if (vAlign == QQuickText::AlignBottom)
471 else if (vAlign == QQuickText::AlignVCenter)
474 q->setBaselineOffset(fm.ascent() + yoff);
476 //### need to comfirm cost of always setting these for richText
477 internalWidthUpdate = true;
479 if (!q->widthValid())
480 iWidth = size.width();
482 q->setImplicitSize(iWidth, size.height());
483 internalWidthUpdate = false;
486 q->setImplicitHeight(size.height());
487 if (layedOutTextRect.size() != previousSize)
488 emit q->contentSizeChanged();
489 updateType = UpdatePaintNode;
493 QQuickTextLine::QQuickTextLine()
494 : QObject(), m_line(0), m_height(0)
498 void QQuickTextLine::setLine(QTextLine *line)
503 void QQuickTextLine::setLineOffset(int offset)
505 m_lineOffset = offset;
508 int QQuickTextLine::number() const
511 return m_line->lineNumber() + m_lineOffset;
515 qreal QQuickTextLine::width() const
518 return m_line->width();
522 void QQuickTextLine::setWidth(qreal width)
525 m_line->setLineWidth(width);
528 qreal QQuickTextLine::height() const
533 return m_line->height();
537 void QQuickTextLine::setHeight(qreal height)
540 m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
544 qreal QQuickTextLine::x() const
551 void QQuickTextLine::setX(qreal x)
554 m_line->setPosition(QPointF(x, m_line->y()));
557 qreal QQuickTextLine::y() const
564 void QQuickTextLine::setY(qreal y)
567 m_line->setPosition(QPointF(m_line->x(), y));
570 void QQuickText::doLayout()
576 bool QQuickTextPrivate::isLineLaidOutConnected()
578 static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
579 return this->isSignalConnected(idx);
582 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
586 #if defined(Q_OS_MAC)
587 if (QThread::currentThread() != paintingThread) {
588 if (!line.lineNumber())
593 textLine = new QQuickTextLine;
594 textLine->setLine(&line);
595 textLine->setY(height);
596 textLine->setHeight(0);
597 textLine->setLineOffset(lineOffset);
599 // use the text item's width by default if it has one and wrap is on
600 if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
601 textLine->setWidth(q->width());
603 textLine->setWidth(INT_MAX);
604 if (lineHeight() != 1.0)
605 textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
607 emit q->lineLaidOut(textLine);
609 height += textLine->height();
611 #if defined(Q_OS_MAC)
612 linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height());
615 if (line.lineNumber() < linesRects.count()) {
616 QRectF r = linesRects.at(line.lineNumber());
617 line.setLineWidth(r.width());
618 line.setPosition(r.topLeft());
624 void QQuickTextPrivate::elideFormats(
625 const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
627 const int end = start + length;
628 QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
629 for (int i = 0; i < formats.count(); ++i) {
630 QTextLayout::FormatRange format = formats.at(i);
631 const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
632 if (formatLength > 0) {
633 format.start = qMax(offset, format.start - start + offset);
634 format.length = formatLength;
635 elidedFormats->append(format);
640 QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
643 nextLine->setLineWidth(INT_MAX);
644 return layout.engine()->elidedText(
645 Qt::TextElideMode(elideMode),
646 QFixed::fromReal(lineWidth),
649 line.textLength() + nextLine->textLength());
651 QString elideText = layout.text().mid(line.textStart(), line.textLength());
653 // QFontMetrics won't help eliding styled text.
654 elideText[elideText.length() - 1] = elideChar;
655 // Appending the elide character may push the line over the maximum width
656 // in which case the elided text will need to be elided.
657 QFontMetricsF metrics(layout.font());
658 if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
659 elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
666 Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
668 Returns the size of the final text. This can be used to position the text vertically (the text is
669 already absolutely positioned horizontally).
672 QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth)
675 layout.setCacheEnabled(true);
677 QTextOption textOption = layout.textOption();
678 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
679 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
680 textOption.setUseDesignMetrics(true);
681 layout.setTextOption(textOption);
682 layout.setFont(font);
684 if ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
685 || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight)) {
686 // we are elided and we have a zero width or height
689 emit q->truncatedChanged();
693 emit q->lineCountChanged();
696 if (requireImplicitWidth) {
697 // Layout to determine the implicit width.
698 layout.beginLayout();
700 for (int i = 0; i < maximumLineCount(); ++i) {
701 QTextLine line = layout.createLine();
706 *naturalWidth = layout.maximumWidth();
707 layout.clearLayout();
709 bool wasInLayout = internalWidthUpdate;
710 internalWidthUpdate = true;
711 q->setImplicitWidth(*naturalWidth);
712 internalWidthUpdate = wasInLayout;
715 QFontMetrics fm(font);
716 qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : fm.height() * lineHeight();
717 return QRect(0, 0, 0, height);
720 qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
721 const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
723 const bool customLayout = isLineLaidOutConnected();
724 const bool wasTruncated = truncated;
726 const bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
727 const bool multilineElide = elideMode == QQuickText::ElideRight
729 && (q->heightValid() || maximumLineCountValid);
730 const bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
732 const bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
733 const bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
734 && (q->heightValid() || (maximumLineCountValid && canWrap));
735 const bool pixelSize = font.pixelSize() != -1;
736 QString layoutText = layout.text();
738 int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
739 int smallFont = fontSizeMode() != QQuickText::FixedSize
740 ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
742 int scaledFontSize = largeFont;
746 QFont scaledFont = font;
749 int visibleCount = 0;
759 int eos = multilengthEos;
761 // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
762 // doesn't fit within the element dimensions.
766 scaledFont.setPixelSize(scaledFontSize);
768 scaledFont.setPointSize(scaledFontSize);
769 layout.setFont(scaledFont);
772 layout.beginLayout();
774 bool wrapped = false;
775 bool truncateHeight = false;
778 int characterCount = 0;
779 int unwrappedLineCount = 1;
780 int maxLineCount = maximumLineCount();
783 line = layout.createLine();
784 for (visibleCount = 1; ; ++visibleCount) {
785 qreal preLayoutHeight = height;
788 setupCustomLineGeometry(line, height);
790 setLineGeometry(line, lineWidth, height);
793 // Elide the previous line if the accumulated height of the text exceeds the height
795 if (multilineElide && height > maxHeight && visibleCount > 1) {
797 if (eos != -1) // There's an abbreviated string available, skip the rest as it's
798 break; // all going to be discarded.
801 truncateHeight = true;
802 height = preLayoutHeight;
804 characterCount = line.textStart() + line.textLength();
806 QTextLine previousLine = layout.lineAt(visibleCount - 2);
807 elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
808 ? elidedText(lineWidth, previousLine, &line)
809 : elidedText(lineWidth, previousLine);
810 elideStart = previousLine.textStart();
811 // elideEnd isn't required for right eliding.
813 // The previous line is the last one visible so move the current one off somewhere
814 // out of the way and back everything up one line.
815 line.setLineWidth(0);
816 line.setPosition(QPointF(FLT_MAX, FLT_MAX));
819 height -= (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : previousLine.height() * lineHeight();
823 QTextLine nextLine = layout.createLine();
824 if (!nextLine.isValid()) {
825 characterCount = line.textStart() + line.textLength();
826 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
827 // Elide a single line of text if its width exceeds the element width.
829 if (eos != -1) // There's an abbreviated string available.
833 height = preLayoutHeight;
834 elideText = layout.engine()->elidedText(
835 Qt::TextElideMode(elideMode),
836 QFixed::fromReal(lineWidth),
840 elideStart = line.textStart();
841 elideEnd = elideStart + line.textLength();
843 br = br.united(line.naturalTextRect());
847 const bool wrappedLine = layoutText.at(nextLine.textStart() - 1) != QChar::LineSeparator;
848 wrapped |= wrappedLine;
851 ++unwrappedLineCount;
853 // Stop if the maximum number of lines has been reached and elide the last line
855 if (visibleCount == maxLineCount) {
857 characterCount = nextLine.textStart() + nextLine.textLength();
859 if (multilineElide) {
861 if (eos != -1) // There's an abbreviated string available
863 height = preLayoutHeight;
864 elideText = wrappedLine
865 ? elidedText(lineWidth, line, &nextLine)
866 : elidedText(lineWidth, line);
867 elideStart = line.textStart();
868 // elideEnd isn't required for right eliding.
870 br = br.united(line.naturalTextRect());
872 nextLine.setLineWidth(0);
873 nextLine.setPosition(QPointF(FLT_MAX, FLT_MAX));
877 br = br.united(line.naturalTextRect());
883 // Save the implicitWidth of the text on the first layout only.
885 *naturalWidth = layout.maximumWidth();
888 if (requireImplicitWidth
889 && characterCount < layoutText.length()
890 && unwrappedLineCount < maxLineCount) {
891 // Use a new layout to get the maximum width for the remaining text. Using a
892 // different layout excludes the truncated text from rendering.
893 QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
894 widthLayout.setTextOption(layout.textOption());
896 for (; unwrappedLineCount <= maxLineCount; ++unwrappedLineCount) {
897 QTextLine line = widthLayout.createLine();
902 *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth());
905 bool wasInLayout = internalWidthUpdate;
906 internalWidthUpdate = true;
907 q->setImplicitWidth(*naturalWidth);
908 internalWidthUpdate = wasInLayout;
910 const qreal oldWidth = lineWidth;
911 lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
912 if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
916 // If the next needs to be elided and there's an abbreviated string available
917 // go back and do another layout with the abbreviated string.
918 if (eos != -1 && elide) {
920 eos = text.indexOf(QLatin1Char('\x9c'), start);
921 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
922 layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
923 layout.setText(layoutText);
924 textHasChanged = true;
928 if (!horizontalFit && !verticalFit)
931 // Try and find a font size that better fits the dimensions of the element.
932 QRectF unelidedRect = br.united(line.naturalTextRect());
935 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
936 largeFont = scaledFontSize - 1;
937 if (smallFont > largeFont)
939 scaledFontSize = (smallFont + largeFont) / 2;
941 scaledFont.setPixelSize(scaledFontSize);
943 scaledFont.setPointSize(scaledFontSize);
945 } else if (!verticalFit) {
946 smallFont = scaledFontSize;
947 if (smallFont == largeFont)
949 scaledFontSize = (smallFont + largeFont + 1) / 2;
954 if (truncateHeight || unelidedRect.height() > maxHeight) {
955 largeFont = scaledFontSize - 1;
956 if (smallFont > largeFont)
958 scaledFontSize = (smallFont + largeFont) / 2;
961 smallFont = scaledFontSize;
962 if (smallFont == largeFont)
964 scaledFontSize = (smallFont + largeFont + 1) / 2;
969 if (eos != multilengthEos)
974 elideLayout = new QTextLayout;
976 QList<QTextLayout::FormatRange> formats;
978 case QQuickText::ElideRight:
979 elideFormats(elideStart, elideText.length() - 1, 0, &formats);
981 case QQuickText::ElideLeft:
982 elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
984 case QQuickText::ElideMiddle: {
985 const int index = elideText.indexOf(elideChar);
987 elideFormats(elideStart, index, 0, &formats);
989 elideEnd - elideText.length() + index + 1,
990 elideText.length() - index - 1,
999 elideLayout->setAdditionalFormats(formats);
1002 elideLayout->setFont(layout.font());
1003 elideLayout->setTextOption(layout.textOption());
1004 elideLayout->setText(elideText);
1005 elideLayout->beginLayout();
1007 QTextLine elidedLine = elideLayout->createLine();
1008 elidedLine.setPosition(QPointF(0, height));
1010 setupCustomLineGeometry(elidedLine, height, line.lineNumber());
1012 setLineGeometry(elidedLine, lineWidth, height);
1014 elideLayout->endLayout();
1016 br = br.united(elidedLine.naturalTextRect());
1018 if (visibleCount > 1)
1019 line.setPosition(QPointF(FLT_MAX, FLT_MAX));
1021 layout.clearLayout();
1028 br.setHeight(height);
1030 //Update the number of visible lines
1031 if (lineCount != visibleCount) {
1032 lineCount = visibleCount;
1033 emit q->lineCountChanged();
1036 if (truncated != wasTruncated)
1037 emit q->truncatedChanged();
1042 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
1045 line.setLineWidth(lineWidth);
1047 if (imgTags.isEmpty()) {
1048 line.setPosition(QPointF(line.position().x(), height));
1049 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
1054 qreal textHeight = line.height();
1055 qreal totalLineHeight = textHeight;
1057 QList<QQuickStyledTextImgTag *> imagesInLine;
1059 foreach (QQuickStyledTextImgTag *image, imgTags) {
1060 if (image->position >= line.textStart() &&
1061 image->position < line.textStart() + line.textLength()) {
1064 QUrl url = q->baseUrl().resolved(image->url);
1065 image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
1066 if (image->pix->isLoading()) {
1067 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
1068 if (!extra.isAllocated() || !extra->nbActiveDownloads)
1069 extra.value().nbActiveDownloads = 0;
1070 extra->nbActiveDownloads++;
1071 } else if (image->pix->isReady()) {
1072 if (!image->size.isValid()) {
1073 image->size = image->pix->implicitSize();
1074 // if the size of the image was not explicitly set, we need to
1075 // call updateLayout() once again.
1076 needToUpdateLayout = true;
1078 } else if (image->pix->isError()) {
1079 qmlInfo(q) << image->pix->error();
1083 qreal ih = qreal(image->size.height());
1084 if (image->align == QQuickStyledTextImgTag::Top)
1086 else if (image->align == QQuickStyledTextImgTag::Middle)
1087 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
1089 image->pos.setY(textHeight - ih);
1090 imagesInLine << image;
1091 textTop = qMax(textTop, qAbs(image->pos.y()));
1095 foreach (QQuickStyledTextImgTag *image, imagesInLine) {
1096 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
1097 image->pos.setX(line.cursorToX(image->position));
1098 image->pos.setY(image->pos.y() + height + textTop);
1099 visibleImgTags << image;
1102 line.setPosition(QPointF(line.position().x(), height + textTop));
1103 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
1107 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1109 void QQuickTextPrivate::ensureDoc()
1111 if (!extra.isAllocated() || !extra->doc) {
1113 extra.value().doc = new QQuickTextDocumentWithImageResources(q);
1114 extra->doc->setDocumentMargin(0);
1115 extra->doc->setBaseUrl(q->baseUrl());
1116 FAST_CONNECT(extra->doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
1121 \qmlclass Text QQuickText
1122 \inqmlmodule QtQuick 2
1123 \ingroup qml-basic-visual-elements
1124 \brief The Text item allows you to add formatted text to a scene.
1127 Text items can display both plain and rich text. For example, red text with
1128 a specific font and size can be defined like this:
1132 text: "Hello World!"
1133 font.family: "Helvetica"
1139 Rich text is defined using HTML-style markup:
1143 text: "<b>Hello</b> <i>World!</i>"
1147 \image declarative-text.png
1149 If height and width are not explicitly set, Text will attempt to determine how
1150 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1151 prefer width to height (all text will be placed on a single line).
1153 The \l elide property can alternatively be used to fit a single line of
1154 plain text to a set width.
1156 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1157 HTML img tags that load remote images, the text is reloaded.
1159 Text provides read-only text. For editable text, see \l TextEdit.
1161 \sa {declarative/text/fonts}{Fonts example}
1163 QQuickText::QQuickText(QQuickItem *parent)
1164 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1170 QQuickText::~QQuickText()
1175 \qmlproperty bool QtQuick2::Text::clip
1176 This property holds whether the text is clipped.
1178 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1180 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1184 \qmlproperty bool QtQuick2::Text::smooth
1186 This property holds whether the text is smoothly scaled or transformed.
1188 Smooth filtering gives better visual quality, but is slower. If
1189 the item is displayed at its natural size, this property has no visual or
1192 \note Generally scaling artifacts are only visible if the item is stationary on
1193 the screen. A common pattern when animating an item is to disable smooth
1194 filtering at the beginning of the animation and reenable it at the conclusion.
1198 \qmlsignal QtQuick2::Text::onLineLaidOut(line)
1200 This handler is called for every line during the layout process.
1201 This gives the opportunity to position and resize a line as it is being laid out.
1202 It can for example be used to create columns or lay out text around objects.
1204 The properties of a line are:
1206 \li number (read-only)
1213 For example, this will move the first 5 lines of a text element by 100 pixels to the right:
1216 if (line.number < 5) {
1217 line.x = line.x + 100
1218 line.width = line.width - 100
1225 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1227 This handler is called when the user clicks on a link embedded in the text.
1228 The link must be in rich text or HTML format and the
1229 \a link string provides access to the particular link.
1231 \snippet doc/src/snippets/qml/text/onLinkActivated.qml 0
1233 The example code will display the text
1234 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1236 Clicking on the highlighted link will output
1237 \tt{http://qt.nokia.com link activated} to the console.
1241 \qmlproperty string QtQuick2::Text::font.family
1243 Sets the family name of the font.
1245 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1246 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1247 If the family isn't available a family will be set using the font matching algorithm.
1251 \qmlproperty bool QtQuick2::Text::font.bold
1253 Sets whether the font weight is bold.
1257 \qmlproperty enumeration QtQuick2::Text::font.weight
1259 Sets the font's weight.
1261 The weight can be one of:
1264 \li Font.Normal - the default
1271 Text { text: "Hello"; font.weight: Font.DemiBold }
1276 \qmlproperty bool QtQuick2::Text::font.italic
1278 Sets whether the font has an italic style.
1282 \qmlproperty bool QtQuick2::Text::font.underline
1284 Sets whether the text is underlined.
1288 \qmlproperty bool QtQuick2::Text::font.strikeout
1290 Sets whether the font has a strikeout style.
1294 \qmlproperty real QtQuick2::Text::font.pointSize
1296 Sets the font size in points. The point size must be greater than zero.
1300 \qmlproperty int QtQuick2::Text::font.pixelSize
1302 Sets the font size in pixels.
1304 Using this function makes the font device dependent.
1305 Use \c pointSize to set the size of the font in a device independent manner.
1309 \qmlproperty real QtQuick2::Text::font.letterSpacing
1311 Sets the letter spacing for the font.
1313 Letter spacing changes the default spacing between individual letters in the font.
1314 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1318 \qmlproperty real QtQuick2::Text::font.wordSpacing
1320 Sets the word spacing for the font.
1322 Word spacing changes the default spacing between individual words.
1323 A positive value increases the word spacing by a corresponding amount of pixels,
1324 while a negative value decreases the inter-word spacing accordingly.
1328 \qmlproperty enumeration QtQuick2::Text::font.capitalization
1330 Sets the capitalization for the text.
1333 \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1334 \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1335 \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1336 \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
1337 \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1341 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1344 QFont QQuickText::font() const
1346 Q_D(const QQuickText);
1347 return d->sourceFont;
1350 void QQuickText::setFont(const QFont &font)
1353 if (d->sourceFont == font)
1356 d->sourceFont = font;
1357 QFont oldFont = d->font;
1360 if (d->font.pointSizeF() != -1) {
1362 qreal size = qRound(d->font.pointSizeF()*2.0);
1363 d->font.setPointSizeF(size/2.0);
1366 if (oldFont != d->font) {
1367 // if the format changes the size of the text
1368 // with headings or <font> tag, we need to re-parse
1369 if (d->formatModifiesFontSize)
1370 d->textHasChanged = true;
1374 emit fontChanged(d->sourceFont);
1378 \qmlproperty string QtQuick2::Text::text
1380 The text to display. Text supports both plain and rich text strings.
1382 The item will try to automatically determine whether the text should
1383 be treated as styled text. This determination is made using Qt::mightBeRichText().
1385 QString QQuickText::text() const
1387 Q_D(const QQuickText);
1391 void QQuickText::setText(const QString &n)
1397 d->richText = d->format == RichText;
1398 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1400 if (isComponentComplete()) {
1403 d->extra->doc->setText(n);
1404 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1406 d->rightToLeftText = d->text.isRightToLeft();
1408 d->determineHorizontalAlignment();
1410 d->textHasChanged = true;
1411 qDeleteAll(d->imgTags);
1414 emit textChanged(d->text);
1418 \qmlproperty color QtQuick2::Text::color
1422 An example of green text defined using hexadecimal notation:
1430 An example of steel blue text defined using an SVG color name:
1438 QColor QQuickText::color() const
1440 Q_D(const QQuickText);
1441 return QColor::fromRgba(d->color);
1444 void QQuickText::setColor(const QColor &color)
1447 QRgb rgb = color.rgba();
1448 if (d->color == rgb)
1452 if (isComponentComplete()) {
1453 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1456 emit colorChanged();
1460 \qmlproperty color QtQuick2::Text::linkColor
1462 The color of links in the text.
1464 This property works with the StyledText \l textFormat, but not with RichText.
1465 Link color in RichText can be specified by including CSS style tags in the
1469 QColor QQuickText::linkColor() const
1471 Q_D(const QQuickText);
1472 return QColor::fromRgba(d->linkColor);
1475 void QQuickText::setLinkColor(const QColor &color)
1478 QRgb rgb = color.rgba();
1479 if (d->linkColor == rgb)
1484 emit linkColorChanged();
1488 \qmlproperty enumeration QtQuick2::Text::style
1490 Set an additional text style.
1492 Supported text styles are:
1494 \li Text.Normal - the default
1502 Text { font.pointSize: 24; text: "Normal" }
1503 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1504 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1505 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1509 \image declarative-textstyle.png
1511 QQuickText::TextStyle QQuickText::style() const
1513 Q_D(const QQuickText);
1517 void QQuickText::setStyle(QQuickText::TextStyle style)
1520 if (d->style == style)
1524 if (isComponentComplete()) {
1525 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1528 emit styleChanged(d->style);
1532 \qmlproperty color QtQuick2::Text::styleColor
1534 Defines the secondary color used by text styles.
1536 \c styleColor is used as the outline color for outlined text, and as the
1537 shadow color for raised or sunken text. If no style has been set, it is not
1541 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1546 QColor QQuickText::styleColor() const
1548 Q_D(const QQuickText);
1549 return QColor::fromRgba(d->styleColor);
1552 void QQuickText::setStyleColor(const QColor &color)
1555 QRgb rgb = color.rgba();
1556 if (d->styleColor == rgb)
1559 d->styleColor = rgb;
1560 if (isComponentComplete()) {
1561 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1564 emit styleColorChanged();
1568 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1569 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1570 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1572 Sets the horizontal and vertical alignment of the text within the Text items
1573 width and height. By default, the text is vertically aligned to the top. Horizontal
1574 alignment follows the natural alignment of the text, for example text that is read
1575 from left to right will be aligned to the left.
1577 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1578 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1579 and \c Text.AlignVCenter.
1581 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1582 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1583 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1586 When using the attached property LayoutMirroring::enabled to mirror application
1587 layouts, the horizontal alignment of text will also be mirrored. However, the property
1588 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1589 of Text, use the read-only property \c effectiveHorizontalAlignment.
1591 QQuickText::HAlignment QQuickText::hAlign() const
1593 Q_D(const QQuickText);
1597 void QQuickText::setHAlign(HAlignment align)
1600 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1601 d->hAlignImplicit = false;
1602 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1606 void QQuickText::resetHAlign()
1609 d->hAlignImplicit = true;
1610 if (isComponentComplete() && d->determineHorizontalAlignment())
1614 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1616 Q_D(const QQuickText);
1617 QQuickText::HAlignment effectiveAlignment = d->hAlign;
1618 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1619 switch (d->hAlign) {
1620 case QQuickText::AlignLeft:
1621 effectiveAlignment = QQuickText::AlignRight;
1623 case QQuickText::AlignRight:
1624 effectiveAlignment = QQuickText::AlignLeft;
1630 return effectiveAlignment;
1633 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1636 if (hAlign != alignment || forceAlign) {
1637 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1640 emit q->horizontalAlignmentChanged(hAlign);
1641 if (oldEffectiveHAlign != q->effectiveHAlign())
1642 emit q->effectiveHorizontalAlignmentChanged();
1648 bool QQuickTextPrivate::determineHorizontalAlignment()
1650 if (hAlignImplicit) {
1651 bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1652 return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1657 void QQuickTextPrivate::mirrorChange()
1660 if (q->isComponentComplete()) {
1661 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1663 emit q->effectiveHorizontalAlignmentChanged();
1668 QQuickText::VAlignment QQuickText::vAlign() const
1670 Q_D(const QQuickText);
1674 void QQuickText::setVAlign(VAlignment align)
1677 if (d->vAlign == align)
1681 emit verticalAlignmentChanged(align);
1685 \qmlproperty enumeration QtQuick2::Text::wrapMode
1687 Set this property to wrap the text to the Text item's width. The text will only
1688 wrap if an explicit width has been set. wrapMode can be one of:
1691 \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
1692 \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
1693 \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1694 \li 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.
1697 QQuickText::WrapMode QQuickText::wrapMode() const
1699 Q_D(const QQuickText);
1703 void QQuickText::setWrapMode(WrapMode mode)
1706 if (mode == d->wrapMode)
1712 emit wrapModeChanged();
1716 \qmlproperty int QtQuick2::Text::lineCount
1718 Returns the number of lines visible in the text item.
1720 This property is not supported for rich text.
1722 \sa maximumLineCount
1724 int QQuickText::lineCount() const
1726 Q_D(const QQuickText);
1727 return d->lineCount;
1731 \qmlproperty bool QtQuick2::Text::truncated
1733 Returns true if the text has been truncated due to \l maximumLineCount
1736 This property is not supported for rich text.
1738 \sa maximumLineCount, elide
1740 bool QQuickText::truncated() const
1742 Q_D(const QQuickText);
1743 return d->truncated;
1747 \qmlproperty int QtQuick2::Text::maximumLineCount
1749 Set this property to limit the number of lines that the text item will show.
1750 If elide is set to Text.ElideRight, the text will be elided appropriately.
1751 By default, this is the value of the largest possible integer.
1753 This property is not supported for rich text.
1755 \sa lineCount, elide
1757 int QQuickText::maximumLineCount() const
1759 Q_D(const QQuickText);
1760 return d->maximumLineCount();
1763 void QQuickText::setMaximumLineCount(int lines)
1767 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1768 if (d->maximumLineCount() != lines) {
1769 d->extra.value().maximumLineCount = lines;
1771 emit maximumLineCountChanged();
1775 void QQuickText::resetMaximumLineCount()
1778 setMaximumLineCount(INT_MAX);
1779 if (d->truncated != false) {
1780 d->truncated = false;
1781 emit truncatedChanged();
1786 \qmlproperty enumeration QtQuick2::Text::textFormat
1788 The way the text property should be displayed.
1790 Supported text formats are:
1793 \li Text.AutoText (default)
1799 If the text format is \c Text.AutoText the text element
1800 will automatically determine whether the text should be treated as
1801 styled text. This determination is made using Qt::mightBeRichText()
1802 which uses a fast and therefore simple heuristic. It mainly checks
1803 whether there is something that looks like a tag before the first
1804 line break. Although the result may be correct for common cases,
1805 there is no guarantee.
1807 Text.StyledText is an optimized format supporting some basic text
1808 styling markup, in the style of html 3.2:
1812 <strong></strong> - bold
1816 <u> - underlined text
1817 <font color="color_name" size="1-7"></font>
1818 <h1> to <h6> - headers
1819 <a href=""> - anchor
1820 <img src="" align="top,middle,bottom" width="" height=""> - inline images
1821 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1822 <pre></pre> - preformatted
1826 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1835 text: "<b>Hello</b> <i>World!</i>"
1839 textFormat: Text.RichText
1840 text: "<b>Hello</b> <i>World!</i>"
1844 textFormat: Text.PlainText
1845 text: "<b>Hello</b> <i>World!</i>"
1849 \li \image declarative-textformat.png
1852 QQuickText::TextFormat QQuickText::textFormat() const
1854 Q_D(const QQuickText);
1858 void QQuickText::setTextFormat(TextFormat format)
1861 if (format == d->format)
1864 bool wasRich = d->richText;
1865 d->richText = format == RichText;
1866 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1868 if (isComponentComplete()) {
1869 if (!wasRich && d->richText) {
1871 d->extra->doc->setText(d->text);
1872 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1874 d->rightToLeftText = d->text.isRightToLeft();
1876 d->determineHorizontalAlignment();
1880 emit textFormatChanged(d->format);
1884 \qmlproperty enumeration QtQuick2::Text::elide
1886 Set this property to elide parts of the text fit to the Text item's width.
1887 The text will only elide if an explicit width has been set.
1889 This property cannot be used with rich text.
1893 \li Text.ElideNone - the default
1895 \li Text.ElideMiddle
1899 If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
1900 text. The text will only elide if \c maximumLineCount, or \c height has been set.
1901 If both \c maximumLineCount and \c height are set, \c maximumLineCount will
1902 apply unless the lines do not fit in the height allowed.
1904 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1905 the first string that fits will be used, otherwise the last will be elided.
1907 Multi-length strings are ordered from longest to shortest, separated by the
1908 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1910 QQuickText::TextElideMode QQuickText::elideMode() const
1912 Q_D(const QQuickText);
1913 return d->elideMode;
1916 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
1919 if (mode == d->elideMode)
1922 d->elideMode = mode;
1925 emit elideModeChanged(mode);
1929 \qmlproperty url QtQuick2::Text::baseUrl
1931 This property specifies a base URL which is used to resolve relative URLs
1934 Urls are resolved to be within the same directory as the target of the base
1935 URL meaning any portion of the path after the last '/' will be ignored.
1938 \header \li Base URL \li Relative URL \li Resolved URL
1939 \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
1940 \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
1941 \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
1942 \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
1943 \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
1944 \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
1945 \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
1948 By default is the url of the Text element.
1951 QUrl QQuickText::baseUrl() const
1953 Q_D(const QQuickText);
1954 if (d->baseUrl.isEmpty()) {
1955 if (QQmlContext *context = qmlContext(this))
1956 const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
1961 void QQuickText::setBaseUrl(const QUrl &url)
1964 if (baseUrl() != url) {
1969 d->extra->doc->setBaseUrl(url);
1971 if (d->styledText) {
1972 d->textHasChanged = true;
1973 qDeleteAll(d->imgTags);
1977 emit baseUrlChanged();
1981 void QQuickText::resetBaseUrl()
1983 if (QQmlContext *context = qmlContext(this))
1984 setBaseUrl(context->baseUrl());
1990 QRectF QQuickText::boundingRect() const
1992 Q_D(const QQuickText);
1994 QRectF rect = d->layedOutTextRect;
1995 if (d->style != Normal)
1996 rect.adjust(-1, 0, 1, 2);
1998 // Could include font max left/right bearings to either side of rectangle.
2001 switch (d->hAlign) {
2006 rect.moveLeft(w - rect.width());
2009 rect.moveLeft((w - rect.width()) / 2);
2014 switch (d->vAlign) {
2018 rect.moveTop(h - rect.height());
2021 rect.moveTop((h - rect.height()) / 2);
2029 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2032 if (d->text.isEmpty()) {
2033 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2037 bool widthChanged = newGeometry.width() != oldGeometry.width();
2038 bool heightChanged = newGeometry.height() != oldGeometry.height();
2039 bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
2040 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2041 bool elide = d->elideMode != QQuickText::ElideNone;
2042 bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
2044 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2045 goto geomChangeDone;
2047 if (leftAligned && !wrapped && !elide && !scaleFont)
2048 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2050 if (!widthChanged && !wrapped && d->singleline && !scaleFont)
2051 goto geomChangeDone; // only height has changed which doesn't affect single line unwrapped text
2053 if (!widthChanged && wrapped && d->elideMode != QQuickText::ElideRight && !scaleFont)
2054 goto geomChangeDone; // only height changed and no multiline eliding.
2056 if (leftAligned && d->elideMode == QQuickText::ElideRight && !d->truncated && d->singleline
2057 && !wrapped && newGeometry.width() > oldGeometry.width() && !scaleFont)
2058 goto geomChangeDone; // Eliding not affected if we're not currently truncated and we get wider.
2060 if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
2062 goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
2063 if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount())
2064 goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
2067 if (d->updateOnComponentComplete || d->textHasChanged) {
2068 // We need to re-elide
2071 // We just need to re-layout
2076 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2079 void QQuickText::triggerPreprocess()
2082 if (d->updateType == QQuickTextPrivate::UpdateNone)
2083 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2087 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2092 if (d->text.isEmpty()) {
2097 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2098 // Update done in preprocess() in the nodes
2099 d->updateType = QQuickTextPrivate::UpdateNone;
2103 d->updateType = QQuickTextPrivate::UpdateNone;
2105 QRectF bounds = boundingRect();
2107 // We need to make sure the layout is done in the current thread
2108 #if defined(Q_OS_MAC)
2109 d->paintingThread = QThread::currentThread();
2110 if (d->layoutThread != d->paintingThread)
2114 QQuickTextNode *node = 0;
2116 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2118 node = static_cast<QQuickTextNode *>(oldNode);
2121 node->deleteContent();
2122 node->setMatrix(QMatrix4x4());
2124 const QColor color = QColor::fromRgba(d->color);
2125 const QColor styleColor = QColor::fromRgba(d->styleColor);
2126 const QColor linkColor = QColor::fromRgba(d->linkColor);
2130 node->addTextDocument(bounds.topLeft(), d->extra->doc, color, d->style, styleColor, linkColor);
2131 } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
2132 node->addTextLayout(QPoint(0, bounds.y()), &d->layout, color, d->style, styleColor, linkColor);
2134 node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, color, d->style, styleColor, linkColor);
2137 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
2138 QQuickPixmap *pix = img->pix;
2139 if (pix && pix->isReady())
2140 node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
2145 void QQuickText::updatePolish()
2148 if (d->updateLayoutOnPolish)
2155 \qmlproperty real QtQuick2::Text::contentWidth
2157 Returns the width of the text, including width past the width
2158 which is covered due to insufficient wrapping if WrapMode is set.
2160 qreal QQuickText::contentWidth() const
2162 Q_D(const QQuickText);
2163 return d->layedOutTextRect.width();
2167 \qmlproperty real QtQuick2::Text::contentHeight
2169 Returns the height of the text, including height past the height
2170 which is covered due to there being more text than fits in the set height.
2172 qreal QQuickText::contentHeight() const
2174 Q_D(const QQuickText);
2175 return d->layedOutTextRect.height();
2179 \qmlproperty real QtQuick2::Text::lineHeight
2181 Sets the line height for the text.
2182 The value can be in pixels or a multiplier depending on lineHeightMode.
2184 The default value is a multiplier of 1.0.
2185 The line height must be a positive value.
2187 qreal QQuickText::lineHeight() const
2189 Q_D(const QQuickText);
2190 return d->lineHeight();
2193 void QQuickText::setLineHeight(qreal lineHeight)
2197 if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
2200 d->extra.value().lineHeight = lineHeight;
2202 emit lineHeightChanged(lineHeight);
2206 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2208 This property determines how the line height is specified.
2209 The possible values are:
2212 \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
2213 line (as a multiplier). For example, set to 2 for double spacing.
2214 \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2217 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2219 Q_D(const QQuickText);
2220 return d->lineHeightMode();
2223 void QQuickText::setLineHeightMode(LineHeightMode mode)
2226 if (mode == d->lineHeightMode())
2229 d->extra.value().lineHeightMode = mode;
2232 emit lineHeightModeChanged(mode);
2236 \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2238 This property specifies how the font size of the displayed text is determined.
2239 The possible values are:
2242 \li Text.FixedSize (default) - The size specified by \l font.pixelSize
2243 or \l font.pointSize is used.
2244 \li Text.HorizontalFit - The largest size up to the size specified that fits
2245 within the width of the item without wrapping is used.
2246 \li Text.VerticalFit - The largest size up to the size specified that fits
2247 the height of the item is used.
2248 \li Text.Fit - The largest size up to the size specified the fits within the
2249 width and height of the item is used.
2252 The font size of fitted text has a minimum bound specified by the
2253 minimumPointSize or minimumPixelSize property and maximum bound specified
2254 by either the \l font.pointSize or \l font.pixelSize properties.
2256 If the text does not fit within the item bounds with the minimum font size
2257 the text will be elided as per the \l elide property.
2260 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2262 Q_D(const QQuickText);
2263 return d->fontSizeMode();
2266 void QQuickText::setFontSizeMode(FontSizeMode mode)
2269 if (d->fontSizeMode() == mode)
2274 d->extra.value().fontSizeMode = mode;
2275 emit fontSizeModeChanged();
2279 \qmlproperty int QtQuick2::Text::minimumPixelSize
2281 This property specifies the minimum font pixel size of text scaled by the
2282 fontSizeMode property.
2284 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2285 property is ignored.
2288 int QQuickText::minimumPixelSize() const
2290 Q_D(const QQuickText);
2291 return d->minimumPixelSize();
2294 void QQuickText::setMinimumPixelSize(int size)
2297 if (d->minimumPixelSize() == size)
2300 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2302 d->extra.value().minimumPixelSize = size;
2303 emit minimumPixelSizeChanged();
2307 \qmlproperty int QtQuick2::Text::minimumPointSize
2309 This property specifies the minimum font point \l size of text scaled by
2310 the fontSizeMode property.
2312 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2313 property is ignored.
2316 int QQuickText::minimumPointSize() const
2318 Q_D(const QQuickText);
2319 return d->minimumPointSize();
2322 void QQuickText::setMinimumPointSize(int size)
2325 if (d->minimumPointSize() == size)
2328 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2330 d->extra.value().minimumPointSize = size;
2331 emit minimumPointSizeChanged();
2335 Returns the number of resources (images) that are being loaded asynchronously.
2337 int QQuickText::resourcesLoading() const
2339 Q_D(const QQuickText);
2340 if (d->richText && d->extra.isAllocated() && d->extra->doc)
2341 return d->extra->doc->resourcesLoading();
2346 void QQuickText::componentComplete()
2349 if (d->updateOnComponentComplete) {
2352 d->extra->doc->setText(d->text);
2353 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
2355 d->rightToLeftText = d->text.isRightToLeft();
2357 d->determineHorizontalAlignment();
2359 QQuickItem::componentComplete();
2360 if (d->updateOnComponentComplete)
2363 // Enable accessibility for text items.
2364 d->setAccessibleFlagAndListener();
2368 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
2371 for (int i = 0; i < layout.lineCount(); ++i) {
2372 QTextLine line = layout.lineAt(i);
2373 if (line.naturalTextRect().contains(mousePos)) {
2374 int charPos = line.xToCursor(mousePos.x());
2375 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
2376 if (formatRange.format.isAnchor()
2377 && charPos >= formatRange.start
2378 && charPos <= formatRange.start + formatRange.length) {
2379 return formatRange.format.anchorHref();
2389 bool QQuickTextPrivate::isLinkActivatedConnected()
2391 static int idx = this->signalIndex("linkActivated(QString)");
2392 return this->isSignalConnected(idx);
2396 void QQuickText::mousePressEvent(QMouseEvent *event)
2401 if (d->isLinkActivatedConnected()) {
2403 link = d->anchorAt(event->localPos());
2404 else if (d->richText) {
2406 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2410 if (link.isEmpty()) {
2411 event->setAccepted(false);
2413 d->extra.value().activeLink = link;
2416 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2418 if (!event->isAccepted())
2419 QQuickItem::mousePressEvent(event);
2424 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2428 // ### confirm the link, and send a signal out
2431 if (d->isLinkActivatedConnected()) {
2433 link = d->anchorAt(event->localPos());
2434 else if (d->richText) {
2436 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2440 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2441 emit linkActivated(d->extra->activeLink);
2443 event->setAccepted(false);
2445 if (!event->isAccepted())
2446 QQuickItem::mouseReleaseEvent(event);