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::updateBaseline(qreal baseline, qreal dy)
391 if (q->heightValid()) {
392 if (vAlign == QQuickText::AlignBottom)
394 else if (vAlign == QQuickText::AlignVCenter)
398 q->setBaselineOffset(baseline + yoff);
401 void QQuickTextPrivate::updateSize()
405 if (!q->isComponentComplete()) {
406 updateOnComponentComplete = true;
410 if (!requireImplicitWidth) {
411 emit q->implicitWidthChanged();
412 // if the implicitWidth is used, then updateSize() has already been called (recursively)
413 if (requireImplicitWidth)
417 if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
418 // How much more expensive is it to just do a full layout on an empty string here?
419 // There may be subtle differences in the height and baseline calculations between
420 // QTextLayout and QFontMetrics and the number of variables that can affect the size
421 // and position of a line is increasing.
422 QFontMetricsF fm(font);
423 qreal fontHeight = qCeil(fm.height()); // QScriptLine and therefore QTextLine rounds up
424 if (!richText) { // line height, so we will as well.
425 fontHeight = lineHeightMode() == QQuickText::FixedHeight
427 : fontHeight * lineHeight();
429 updateBaseline(fm.ascent(), q->height() - fontHeight);
430 q->setImplicitSize(0, fontHeight);
431 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
432 emit q->contentSizeChanged();
433 updateType = UpdatePaintNode;
438 qreal naturalWidth = 0;
441 QSizeF previousSize = layedOutTextRect.size();
442 #if defined(Q_OS_MAC)
443 layoutThread = QThread::currentThread();
446 //setup instance of QTextLayout for all cases other than richtext
449 QRectF textRect = setupTextLayout(&naturalWidth, &baseline);
451 if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
452 return; // get this far we'll get a warning to that effect if it is.
454 layedOutTextRect = textRect;
455 size = textRect.size();
456 updateBaseline(baseline, q->height() - size.height());
458 singleline = false; // richtext can't elide or be optimized for single-line case
460 extra->doc->setDefaultFont(font);
461 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
462 if (rightToLeftText) {
463 if (horizontalAlignment == QQuickText::AlignLeft)
464 horizontalAlignment = QQuickText::AlignRight;
465 else if (horizontalAlignment == QQuickText::AlignRight)
466 horizontalAlignment = QQuickText::AlignLeft;
469 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
470 option.setWrapMode(QTextOption::WrapMode(wrapMode));
471 option.setUseDesignMetrics(true);
472 extra->doc->setDefaultTextOption(option);
473 if (requireImplicitWidth && q->widthValid()) {
474 extra->doc->setTextWidth(-1);
475 naturalWidth = extra->doc->idealWidth();
476 const bool wasInLayout = internalWidthUpdate;
477 internalWidthUpdate = true;
478 q->setImplicitWidth(naturalWidth);
479 internalWidthUpdate = wasInLayout;
481 if (internalWidthUpdate)
483 if (wrapMode != QQuickText::NoWrap && q->widthValid())
484 extra->doc->setTextWidth(q->width());
486 extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
487 QSizeF dsize = extra->doc->size();
488 layedOutTextRect = QRectF(QPointF(0,0), dsize);
489 size = QSizeF(extra->doc->idealWidth(),dsize.height());
491 QFontMetricsF fm(font);
492 updateBaseline(fm.ascent(), q->height() - size.height());
495 //### need to comfirm cost of always setting these for richText
496 internalWidthUpdate = true;
498 if (!q->widthValid())
499 iWidth = size.width();
501 q->setImplicitSize(iWidth, size.height());
502 internalWidthUpdate = false;
505 q->setImplicitHeight(size.height());
506 if (layedOutTextRect.size() != previousSize)
507 emit q->contentSizeChanged();
508 updateType = UpdatePaintNode;
512 QQuickTextLine::QQuickTextLine()
513 : QObject(), m_line(0), m_height(0)
517 void QQuickTextLine::setLine(QTextLine *line)
522 void QQuickTextLine::setLineOffset(int offset)
524 m_lineOffset = offset;
527 int QQuickTextLine::number() const
530 return m_line->lineNumber() + m_lineOffset;
534 qreal QQuickTextLine::width() const
537 return m_line->width();
541 void QQuickTextLine::setWidth(qreal width)
544 m_line->setLineWidth(width);
547 qreal QQuickTextLine::height() const
552 return m_line->height();
556 void QQuickTextLine::setHeight(qreal height)
559 m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
563 qreal QQuickTextLine::x() const
570 void QQuickTextLine::setX(qreal x)
573 m_line->setPosition(QPointF(x, m_line->y()));
576 qreal QQuickTextLine::y() const
583 void QQuickTextLine::setY(qreal y)
586 m_line->setPosition(QPointF(m_line->x(), y));
589 void QQuickText::doLayout()
595 bool QQuickTextPrivate::isLineLaidOutConnected()
598 IS_SIGNAL_CONNECTED(q, "lineLaidOut(QQuickTextLine*)");
601 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
605 #if defined(Q_OS_MAC)
606 if (QThread::currentThread() != paintingThread) {
607 if (!line.lineNumber())
612 textLine = new QQuickTextLine;
613 textLine->setLine(&line);
614 textLine->setY(height);
615 textLine->setHeight(0);
616 textLine->setLineOffset(lineOffset);
618 // use the text item's width by default if it has one and wrap is on
619 if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
620 textLine->setWidth(q->width());
622 textLine->setWidth(INT_MAX);
623 if (lineHeight() != 1.0)
624 textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
626 emit q->lineLaidOut(textLine);
628 height += textLine->height();
630 #if defined(Q_OS_MAC)
631 linesRects << QRectF(textLine->x(), textLine->y(), textLine->width(), textLine->height());
634 if (line.lineNumber() < linesRects.count()) {
635 QRectF r = linesRects.at(line.lineNumber());
636 line.setLineWidth(r.width());
637 line.setPosition(r.topLeft());
643 void QQuickTextPrivate::elideFormats(
644 const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
646 const int end = start + length;
647 QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
648 for (int i = 0; i < formats.count(); ++i) {
649 QTextLayout::FormatRange format = formats.at(i);
650 const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
651 if (formatLength > 0) {
652 format.start = qMax(offset, format.start - start + offset);
653 format.length = formatLength;
654 elidedFormats->append(format);
659 QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
662 nextLine->setLineWidth(INT_MAX);
663 return layout.engine()->elidedText(
664 Qt::TextElideMode(elideMode),
665 QFixed::fromReal(lineWidth),
668 line.textLength() + nextLine->textLength());
670 QString elideText = layout.text().mid(line.textStart(), line.textLength());
672 // QFontMetrics won't help eliding styled text.
673 elideText[elideText.length() - 1] = elideChar;
674 // Appending the elide character may push the line over the maximum width
675 // in which case the elided text will need to be elided.
676 QFontMetricsF metrics(layout.font());
677 if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
678 elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
685 Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
687 Returns the size of the final text. This can be used to position the text vertically (the text is
688 already absolutely positioned horizontally).
691 QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth, qreal *const baseline)
694 layout.setCacheEnabled(true);
696 QTextOption textOption = layout.textOption();
697 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
698 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
699 textOption.setUseDesignMetrics(true);
700 layout.setTextOption(textOption);
701 layout.setFont(font);
703 if (!requireImplicitWidth
704 && ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
705 || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight))) {
706 // we are elided and we have a zero width or height
709 emit q->truncatedChanged();
713 emit q->lineCountChanged();
716 QFontMetricsF fm(font);
717 qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : fm.height() * lineHeight();
718 *baseline = fm.ascent();
719 return QRectF(0, 0, 0, height);
722 qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
723 const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
725 const bool customLayout = isLineLaidOutConnected();
726 const bool wasTruncated = truncated;
728 bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
729 bool multilineElide = elideMode == QQuickText::ElideRight
731 && (q->heightValid() || maximumLineCountValid);
732 bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
734 bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
735 bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
736 && (q->heightValid() || (maximumLineCountValid && canWrap));
738 const bool pixelSize = font.pixelSize() != -1;
739 QString layoutText = layout.text();
741 int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
742 int smallFont = fontSizeMode() != QQuickText::FixedSize
743 ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
745 int scaledFontSize = largeFont;
749 QFont scaledFont = font;
752 int visibleCount = 0;
762 int eos = multilengthEos;
764 // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
765 // doesn't fit within the element dimensions.
769 scaledFont.setPixelSize(scaledFontSize);
771 scaledFont.setPointSize(scaledFontSize);
772 layout.setFont(scaledFont);
775 layout.beginLayout();
777 bool wrapped = false;
778 bool truncateHeight = false;
781 int characterCount = 0;
782 int unwrappedLineCount = 1;
783 int maxLineCount = maximumLineCount();
786 line = layout.createLine();
787 for (visibleCount = 1; ; ++visibleCount) {
788 qreal preLayoutHeight = height;
791 setupCustomLineGeometry(line, height);
793 setLineGeometry(line, lineWidth, height);
796 // Elide the previous line if the accumulated height of the text exceeds the height
798 if (multilineElide && height > maxHeight && visibleCount > 1) {
800 if (eos != -1) // There's an abbreviated string available, skip the rest as it's
801 break; // all going to be discarded.
804 truncateHeight = true;
805 height = preLayoutHeight;
807 characterCount = line.textStart() + line.textLength();
810 QTextLine previousLine = layout.lineAt(visibleCount - 1);
811 elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
812 ? elidedText(lineWidth, previousLine, &line)
813 : elidedText(lineWidth, previousLine);
814 elideStart = previousLine.textStart();
815 // elideEnd isn't required for right eliding.
818 height -= (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : previousLine.height() * lineHeight();
822 QTextLine nextLine = layout.createLine();
823 if (!nextLine.isValid()) {
824 characterCount = line.textStart() + line.textLength();
825 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
826 // Elide a single line of text if its width exceeds the element width.
828 if (eos != -1) // There's an abbreviated string available.
832 height = preLayoutHeight;
833 elideText = layout.engine()->elidedText(
834 Qt::TextElideMode(elideMode),
835 QFixed::fromReal(lineWidth),
839 elideStart = line.textStart();
840 elideEnd = elideStart + line.textLength();
842 br = br.united(line.naturalTextRect());
846 const bool wrappedLine = layoutText.at(nextLine.textStart() - 1) != QChar::LineSeparator;
847 wrapped |= wrappedLine;
850 ++unwrappedLineCount;
852 // Stop if the maximum number of lines has been reached and elide the last line
854 if (visibleCount == maxLineCount) {
856 characterCount = nextLine.textStart() + nextLine.textLength();
858 if (multilineElide) {
860 if (eos != -1) // There's an abbreviated string available
862 height = preLayoutHeight;
863 elideText = wrappedLine
864 ? elidedText(lineWidth, line, &nextLine)
865 : elidedText(lineWidth, line);
866 elideStart = line.textStart();
867 // elideEnd isn't required for right eliding.
869 br = br.united(line.naturalTextRect());
874 br = br.united(line.naturalTextRect());
880 // Save the implicitWidth of the text on the first layout only.
882 *naturalWidth = layout.maximumWidth();
885 if (requireImplicitWidth
886 && characterCount < layoutText.length()
887 && unwrappedLineCount < maxLineCount) {
888 // Use a new layout to get the maximum width for the remaining text. Using a
889 // different layout excludes the truncated text from rendering.
890 QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
891 widthLayout.setTextOption(layout.textOption());
893 widthLayout.beginLayout();
894 for (; unwrappedLineCount <= maxLineCount; ++unwrappedLineCount) {
895 QTextLine line = widthLayout.createLine();
899 widthLayout.endLayout();
900 *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth());
903 bool wasInLayout = internalWidthUpdate;
904 internalWidthUpdate = true;
905 q->setImplicitWidth(*naturalWidth);
906 internalWidthUpdate = wasInLayout;
908 singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
909 multilineElide = elideMode == QQuickText::ElideRight
911 && (q->heightValid() || maximumLineCountValid);
912 canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
914 horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
915 verticalFit = fontSizeMode() & QQuickText::VerticalFit
916 && (q->heightValid() || (maximumLineCountValid && canWrap));
918 const qreal oldWidth = lineWidth;
919 lineWidth = q->widthValid() && q->width() > 0 ? q->width() : *naturalWidth;
920 if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
922 // If the horizontal alignment is not left and the width was not valid we need to relayout
923 // now that we know the maximum line width.
924 if (!q->widthValid() && maxLineCount > 1 && q->effectiveHAlign() != QQuickText::AlignLeft)
928 // If the next needs to be elided and there's an abbreviated string available
929 // go back and do another layout with the abbreviated string.
930 if (eos != -1 && elide) {
932 eos = text.indexOf(QLatin1Char('\x9c'), start);
933 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
934 layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
935 layout.setText(layoutText);
936 textHasChanged = true;
940 if (!horizontalFit && !verticalFit)
943 // Try and find a font size that better fits the dimensions of the element.
944 QRectF unelidedRect = br.united(line.naturalTextRect());
947 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
948 largeFont = scaledFontSize - 1;
949 if (smallFont > largeFont)
951 scaledFontSize = (smallFont + largeFont) / 2;
953 scaledFont.setPixelSize(scaledFontSize);
955 scaledFont.setPointSize(scaledFontSize);
957 } else if (!verticalFit) {
958 smallFont = scaledFontSize;
959 if (smallFont == largeFont)
961 scaledFontSize = (smallFont + largeFont + 1) / 2;
966 if (truncateHeight || unelidedRect.height() > maxHeight) {
967 largeFont = scaledFontSize - 1;
968 if (smallFont > largeFont)
970 scaledFontSize = (smallFont + largeFont) / 2;
973 smallFont = scaledFontSize;
974 if (smallFont == largeFont)
976 scaledFontSize = (smallFont + largeFont + 1) / 2;
981 if (eos != multilengthEos)
986 elideLayout = new QTextLayout;
987 elideLayout->setCacheEnabled(true);
990 QList<QTextLayout::FormatRange> formats;
992 case QQuickText::ElideRight:
993 elideFormats(elideStart, elideText.length() - 1, 0, &formats);
995 case QQuickText::ElideLeft:
996 elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
998 case QQuickText::ElideMiddle: {
999 const int index = elideText.indexOf(elideChar);
1001 elideFormats(elideStart, index, 0, &formats);
1003 elideEnd - elideText.length() + index + 1,
1004 elideText.length() - index - 1,
1013 elideLayout->setAdditionalFormats(formats);
1016 elideLayout->setFont(layout.font());
1017 elideLayout->setTextOption(layout.textOption());
1018 elideLayout->setText(elideText);
1019 elideLayout->beginLayout();
1021 QTextLine elidedLine = elideLayout->createLine();
1022 elidedLine.setPosition(QPointF(0, height));
1024 setupCustomLineGeometry(elidedLine, height, line.lineNumber());
1026 setLineGeometry(elidedLine, lineWidth, height);
1028 elideLayout->endLayout();
1030 br = br.united(elidedLine.naturalTextRect());
1032 if (visibleCount == 1)
1033 layout.clearLayout();
1039 QTextLine firstLine = visibleCount == 1 && elideLayout
1040 ? elideLayout->lineAt(0)
1042 Q_ASSERT(firstLine.isValid());
1043 *baseline = firstLine.y() + firstLine.ascent();
1046 br.setHeight(height);
1048 //Update the number of visible lines
1049 if (lineCount != visibleCount) {
1050 lineCount = visibleCount;
1051 emit q->lineCountChanged();
1054 if (truncated != wasTruncated)
1055 emit q->truncatedChanged();
1060 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
1063 line.setLineWidth(lineWidth);
1065 if (imgTags.isEmpty()) {
1066 line.setPosition(QPointF(line.position().x(), height));
1067 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
1072 qreal textHeight = line.height();
1073 qreal totalLineHeight = textHeight;
1075 QList<QQuickStyledTextImgTag *> imagesInLine;
1077 foreach (QQuickStyledTextImgTag *image, imgTags) {
1078 if (image->position >= line.textStart() &&
1079 image->position < line.textStart() + line.textLength()) {
1082 QUrl url = q->baseUrl().resolved(image->url);
1083 image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
1084 if (image->pix->isLoading()) {
1085 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
1086 if (!extra.isAllocated() || !extra->nbActiveDownloads)
1087 extra.value().nbActiveDownloads = 0;
1088 extra->nbActiveDownloads++;
1089 } else if (image->pix->isReady()) {
1090 if (!image->size.isValid()) {
1091 image->size = image->pix->implicitSize();
1092 // if the size of the image was not explicitly set, we need to
1093 // call updateLayout() once again.
1094 needToUpdateLayout = true;
1096 } else if (image->pix->isError()) {
1097 qmlInfo(q) << image->pix->error();
1101 qreal ih = qreal(image->size.height());
1102 if (image->align == QQuickStyledTextImgTag::Top)
1104 else if (image->align == QQuickStyledTextImgTag::Middle)
1105 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
1107 image->pos.setY(textHeight - ih);
1108 imagesInLine << image;
1109 textTop = qMax(textTop, qAbs(image->pos.y()));
1113 foreach (QQuickStyledTextImgTag *image, imagesInLine) {
1114 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
1115 image->pos.setX(line.cursorToX(image->position));
1116 image->pos.setY(image->pos.y() + height + textTop);
1117 visibleImgTags << image;
1120 line.setPosition(QPointF(line.position().x(), height + textTop));
1121 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
1125 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1127 void QQuickTextPrivate::ensureDoc()
1129 if (!extra.isAllocated() || !extra->doc) {
1131 extra.value().doc = new QQuickTextDocumentWithImageResources(q);
1132 extra->doc->setDocumentMargin(0);
1133 extra->doc->setBaseUrl(q->baseUrl());
1134 qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
1135 q, QQuickText, SLOT(q_imagesLoaded()));
1140 \qmlclass Text QQuickText
1141 \inqmlmodule QtQuick 2
1142 \ingroup qtquick-text
1144 \brief Specifies how to add formatted text to a scene
1146 Text items can display both plain and rich text. For example, red text with
1147 a specific font and size can be defined like this:
1151 text: "Hello World!"
1152 font.family: "Helvetica"
1158 Rich text is defined using HTML-style markup:
1162 text: "<b>Hello</b> <i>World!</i>"
1166 \image declarative-text.png
1168 If height and width are not explicitly set, Text will attempt to determine how
1169 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1170 prefer width to height (all text will be placed on a single line).
1172 The \l elide property can alternatively be used to fit a single line of
1173 plain text to a set width.
1175 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1176 HTML img tags that load remote images, the text is reloaded.
1178 Text provides read-only text. For editable text, see \l TextEdit.
1180 \sa {declarative/text/fonts}{Fonts example}
1182 QQuickText::QQuickText(QQuickItem *parent)
1183 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1189 QQuickText::~QQuickText()
1194 \qmlproperty bool QtQuick2::Text::clip
1195 This property holds whether the text is clipped.
1197 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1199 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1203 \qmlproperty bool QtQuick2::Text::smooth
1205 This property holds whether the text is smoothly scaled or transformed.
1207 Smooth filtering gives better visual quality, but is slower. If
1208 the item is displayed at its natural size, this property has no visual or
1211 \note Generally scaling artifacts are only visible if the item is stationary on
1212 the screen. A common pattern when animating an item is to disable smooth
1213 filtering at the beginning of the animation and reenable it at the conclusion.
1217 \qmlsignal QtQuick2::Text::onLineLaidOut(line)
1219 This handler is called for every line during the layout process.
1220 This gives the opportunity to position and resize a line as it is being laid out.
1221 It can for example be used to create columns or lay out text around objects.
1223 The properties of a line are:
1225 \li number (read-only)
1232 For example, this will move the first 5 lines of a text element by 100 pixels to the right:
1235 if (line.number < 5) {
1236 line.x = line.x + 100
1237 line.width = line.width - 100
1244 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1246 This handler is called when the user clicks on a link embedded in the text.
1247 The link must be in rich text or HTML format and the
1248 \a link string provides access to the particular link.
1250 \snippet qml/text/onLinkActivated.qml 0
1252 The example code will display the text
1253 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1255 Clicking on the highlighted link will output
1256 \tt{http://qt.nokia.com link activated} to the console.
1260 \qmlproperty string QtQuick2::Text::font.family
1262 Sets the family name of the font.
1264 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1265 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1266 If the family isn't available a family will be set using the font matching algorithm.
1270 \qmlproperty bool QtQuick2::Text::font.bold
1272 Sets whether the font weight is bold.
1276 \qmlproperty enumeration QtQuick2::Text::font.weight
1278 Sets the font's weight.
1280 The weight can be one of:
1283 \li Font.Normal - the default
1290 Text { text: "Hello"; font.weight: Font.DemiBold }
1295 \qmlproperty bool QtQuick2::Text::font.italic
1297 Sets whether the font has an italic style.
1301 \qmlproperty bool QtQuick2::Text::font.underline
1303 Sets whether the text is underlined.
1307 \qmlproperty bool QtQuick2::Text::font.strikeout
1309 Sets whether the font has a strikeout style.
1313 \qmlproperty real QtQuick2::Text::font.pointSize
1315 Sets the font size in points. The point size must be greater than zero.
1319 \qmlproperty int QtQuick2::Text::font.pixelSize
1321 Sets the font size in pixels.
1323 Using this function makes the font device dependent.
1324 Use \c pointSize to set the size of the font in a device independent manner.
1328 \qmlproperty real QtQuick2::Text::font.letterSpacing
1330 Sets the letter spacing for the font.
1332 Letter spacing changes the default spacing between individual letters in the font.
1333 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1337 \qmlproperty real QtQuick2::Text::font.wordSpacing
1339 Sets the word spacing for the font.
1341 Word spacing changes the default spacing between individual words.
1342 A positive value increases the word spacing by a corresponding amount of pixels,
1343 while a negative value decreases the inter-word spacing accordingly.
1347 \qmlproperty enumeration QtQuick2::Text::font.capitalization
1349 Sets the capitalization for the text.
1352 \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1353 \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1354 \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1355 \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
1356 \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1360 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1363 QFont QQuickText::font() const
1365 Q_D(const QQuickText);
1366 return d->sourceFont;
1369 void QQuickText::setFont(const QFont &font)
1372 if (d->sourceFont == font)
1375 d->sourceFont = font;
1376 QFont oldFont = d->font;
1379 if (d->font.pointSizeF() != -1) {
1381 qreal size = qRound(d->font.pointSizeF()*2.0);
1382 d->font.setPointSizeF(size/2.0);
1385 if (oldFont != d->font) {
1386 // if the format changes the size of the text
1387 // with headings or <font> tag, we need to re-parse
1388 if (d->formatModifiesFontSize)
1389 d->textHasChanged = true;
1393 emit fontChanged(d->sourceFont);
1397 \qmlproperty string QtQuick2::Text::text
1399 The text to display. Text supports both plain and rich text strings.
1401 The item will try to automatically determine whether the text should
1402 be treated as styled text. This determination is made using Qt::mightBeRichText().
1404 QString QQuickText::text() const
1406 Q_D(const QQuickText);
1410 void QQuickText::setText(const QString &n)
1416 d->richText = d->format == RichText;
1417 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1419 if (isComponentComplete()) {
1422 d->extra->doc->setText(n);
1423 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1425 d->rightToLeftText = d->text.isRightToLeft();
1427 d->determineHorizontalAlignment();
1429 d->textHasChanged = true;
1430 qDeleteAll(d->imgTags);
1433 emit textChanged(d->text);
1437 \qmlproperty color QtQuick2::Text::color
1441 An example of green text defined using hexadecimal notation:
1449 An example of steel blue text defined using an SVG color name:
1457 QColor QQuickText::color() const
1459 Q_D(const QQuickText);
1460 return QColor::fromRgba(d->color);
1463 void QQuickText::setColor(const QColor &color)
1466 QRgb rgb = color.rgba();
1467 if (d->color == rgb)
1471 if (isComponentComplete()) {
1472 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1475 emit colorChanged();
1479 \qmlproperty color QtQuick2::Text::linkColor
1481 The color of links in the text.
1483 This property works with the StyledText \l textFormat, but not with RichText.
1484 Link color in RichText can be specified by including CSS style tags in the
1488 QColor QQuickText::linkColor() const
1490 Q_D(const QQuickText);
1491 return QColor::fromRgba(d->linkColor);
1494 void QQuickText::setLinkColor(const QColor &color)
1497 QRgb rgb = color.rgba();
1498 if (d->linkColor == rgb)
1503 emit linkColorChanged();
1507 \qmlproperty enumeration QtQuick2::Text::style
1509 Set an additional text style.
1511 Supported text styles are:
1513 \li Text.Normal - the default
1521 Text { font.pointSize: 24; text: "Normal" }
1522 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1523 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1524 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1528 \image declarative-textstyle.png
1530 QQuickText::TextStyle QQuickText::style() const
1532 Q_D(const QQuickText);
1536 void QQuickText::setStyle(QQuickText::TextStyle style)
1539 if (d->style == style)
1543 if (isComponentComplete()) {
1544 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1547 emit styleChanged(d->style);
1551 \qmlproperty color QtQuick2::Text::styleColor
1553 Defines the secondary color used by text styles.
1555 \c styleColor is used as the outline color for outlined text, and as the
1556 shadow color for raised or sunken text. If no style has been set, it is not
1560 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1565 QColor QQuickText::styleColor() const
1567 Q_D(const QQuickText);
1568 return QColor::fromRgba(d->styleColor);
1571 void QQuickText::setStyleColor(const QColor &color)
1574 QRgb rgb = color.rgba();
1575 if (d->styleColor == rgb)
1578 d->styleColor = rgb;
1579 if (isComponentComplete()) {
1580 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1583 emit styleColorChanged();
1587 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1588 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1589 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1591 Sets the horizontal and vertical alignment of the text within the Text items
1592 width and height. By default, the text is vertically aligned to the top. Horizontal
1593 alignment follows the natural alignment of the text, for example text that is read
1594 from left to right will be aligned to the left.
1596 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1597 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1598 and \c Text.AlignVCenter.
1600 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1601 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1602 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1605 When using the attached property LayoutMirroring::enabled to mirror application
1606 layouts, the horizontal alignment of text will also be mirrored. However, the property
1607 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1608 of Text, use the read-only property \c effectiveHorizontalAlignment.
1610 QQuickText::HAlignment QQuickText::hAlign() const
1612 Q_D(const QQuickText);
1616 void QQuickText::setHAlign(HAlignment align)
1619 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1620 d->hAlignImplicit = false;
1621 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1625 void QQuickText::resetHAlign()
1628 d->hAlignImplicit = true;
1629 if (isComponentComplete() && d->determineHorizontalAlignment())
1633 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1635 Q_D(const QQuickText);
1636 QQuickText::HAlignment effectiveAlignment = d->hAlign;
1637 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1638 switch (d->hAlign) {
1639 case QQuickText::AlignLeft:
1640 effectiveAlignment = QQuickText::AlignRight;
1642 case QQuickText::AlignRight:
1643 effectiveAlignment = QQuickText::AlignLeft;
1649 return effectiveAlignment;
1652 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1655 if (hAlign != alignment || forceAlign) {
1656 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1659 emit q->horizontalAlignmentChanged(hAlign);
1660 if (oldEffectiveHAlign != q->effectiveHAlign())
1661 emit q->effectiveHorizontalAlignmentChanged();
1667 bool QQuickTextPrivate::determineHorizontalAlignment()
1669 if (hAlignImplicit) {
1670 bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1671 return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1676 void QQuickTextPrivate::mirrorChange()
1679 if (q->isComponentComplete()) {
1680 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1682 emit q->effectiveHorizontalAlignmentChanged();
1687 QQuickText::VAlignment QQuickText::vAlign() const
1689 Q_D(const QQuickText);
1693 void QQuickText::setVAlign(VAlignment align)
1696 if (d->vAlign == align)
1700 emit verticalAlignmentChanged(align);
1704 \qmlproperty enumeration QtQuick2::Text::wrapMode
1706 Set this property to wrap the text to the Text item's width. The text will only
1707 wrap if an explicit width has been set. wrapMode can be one of:
1710 \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
1711 \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
1712 \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1713 \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.
1716 QQuickText::WrapMode QQuickText::wrapMode() const
1718 Q_D(const QQuickText);
1722 void QQuickText::setWrapMode(WrapMode mode)
1725 if (mode == d->wrapMode)
1731 emit wrapModeChanged();
1735 \qmlproperty int QtQuick2::Text::lineCount
1737 Returns the number of lines visible in the text item.
1739 This property is not supported for rich text.
1741 \sa maximumLineCount
1743 int QQuickText::lineCount() const
1745 Q_D(const QQuickText);
1746 return d->lineCount;
1750 \qmlproperty bool QtQuick2::Text::truncated
1752 Returns true if the text has been truncated due to \l maximumLineCount
1755 This property is not supported for rich text.
1757 \sa maximumLineCount, elide
1759 bool QQuickText::truncated() const
1761 Q_D(const QQuickText);
1762 return d->truncated;
1766 \qmlproperty int QtQuick2::Text::maximumLineCount
1768 Set this property to limit the number of lines that the text item will show.
1769 If elide is set to Text.ElideRight, the text will be elided appropriately.
1770 By default, this is the value of the largest possible integer.
1772 This property is not supported for rich text.
1774 \sa lineCount, elide
1776 int QQuickText::maximumLineCount() const
1778 Q_D(const QQuickText);
1779 return d->maximumLineCount();
1782 void QQuickText::setMaximumLineCount(int lines)
1786 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1787 if (d->maximumLineCount() != lines) {
1788 d->extra.value().maximumLineCount = lines;
1790 emit maximumLineCountChanged();
1794 void QQuickText::resetMaximumLineCount()
1797 setMaximumLineCount(INT_MAX);
1798 if (d->truncated != false) {
1799 d->truncated = false;
1800 emit truncatedChanged();
1805 \qmlproperty enumeration QtQuick2::Text::textFormat
1807 The way the text property should be displayed.
1809 Supported text formats are:
1812 \li Text.AutoText (default)
1818 If the text format is \c Text.AutoText the text element
1819 will automatically determine whether the text should be treated as
1820 styled text. This determination is made using Qt::mightBeRichText()
1821 which uses a fast and therefore simple heuristic. It mainly checks
1822 whether there is something that looks like a tag before the first
1823 line break. Although the result may be correct for common cases,
1824 there is no guarantee.
1826 Text.StyledText is an optimized format supporting some basic text
1827 styling markup, in the style of html 3.2:
1831 <strong></strong> - bold
1835 <u> - underlined text
1836 <font color="color_name" size="1-7"></font>
1837 <h1> to <h6> - headers
1838 <a href=""> - anchor
1839 <img src="" align="top,middle,bottom" width="" height=""> - inline images
1840 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1841 <pre></pre> - preformatted
1845 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1854 text: "<b>Hello</b> <i>World!</i>"
1858 textFormat: Text.RichText
1859 text: "<b>Hello</b> <i>World!</i>"
1863 textFormat: Text.PlainText
1864 text: "<b>Hello</b> <i>World!</i>"
1868 \li \image declarative-textformat.png
1871 QQuickText::TextFormat QQuickText::textFormat() const
1873 Q_D(const QQuickText);
1877 void QQuickText::setTextFormat(TextFormat format)
1880 if (format == d->format)
1883 bool wasRich = d->richText;
1884 d->richText = format == RichText;
1885 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1887 if (isComponentComplete()) {
1888 if (!wasRich && d->richText) {
1890 d->extra->doc->setText(d->text);
1891 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
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 \li Text.ElideNone - the default
1914 \li Text.ElideMiddle
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(mode);
1948 \qmlproperty url QtQuick2::Text::baseUrl
1950 This property specifies a base URL which is used to resolve relative URLs
1953 Urls are resolved to be within the same directory as the target of the base
1954 URL meaning any portion of the path after the last '/' will be ignored.
1957 \header \li Base URL \li Relative URL \li Resolved URL
1958 \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
1959 \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
1960 \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
1961 \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
1962 \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
1963 \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
1964 \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
1967 By default is the url of the Text element.
1970 QUrl QQuickText::baseUrl() const
1972 Q_D(const QQuickText);
1973 if (d->baseUrl.isEmpty()) {
1974 if (QQmlContext *context = qmlContext(this))
1975 const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
1980 void QQuickText::setBaseUrl(const QUrl &url)
1983 if (baseUrl() != url) {
1988 d->extra->doc->setBaseUrl(url);
1990 if (d->styledText) {
1991 d->textHasChanged = true;
1992 qDeleteAll(d->imgTags);
1996 emit baseUrlChanged();
2000 void QQuickText::resetBaseUrl()
2002 if (QQmlContext *context = qmlContext(this))
2003 setBaseUrl(context->baseUrl());
2009 QRectF QQuickText::boundingRect() const
2011 Q_D(const QQuickText);
2013 QRectF rect = d->layedOutTextRect;
2014 if (d->style != Normal)
2015 rect.adjust(-1, 0, 1, 2);
2017 // Could include font max left/right bearings to either side of rectangle.
2020 switch (d->hAlign) {
2025 rect.moveLeft(w - rect.width());
2028 rect.moveLeft((w - rect.width()) / 2);
2033 switch (d->vAlign) {
2037 rect.moveTop(h - rect.height());
2040 rect.moveTop((h - rect.height()) / 2);
2047 QRectF QQuickText::clipRect() const
2049 Q_D(const QQuickText);
2051 QRectF rect = QQuickImplicitSizeItem::clipRect();
2052 if (d->style != Normal)
2053 rect.adjust(-1, 0, 1, 2);
2058 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2061 if (d->text.isEmpty()) {
2062 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2066 bool widthChanged = newGeometry.width() != oldGeometry.width();
2067 bool heightChanged = newGeometry.height() != oldGeometry.height();
2068 bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
2069 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2070 bool elide = d->elideMode != QQuickText::ElideNone;
2071 bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
2073 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2074 goto geomChangeDone;
2076 if (leftAligned && !wrapped && !elide && !scaleFont)
2077 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2079 if (!widthChanged && !wrapped && d->singleline && !scaleFont)
2080 goto geomChangeDone; // only height has changed which doesn't affect single line unwrapped text
2082 if (!widthChanged && wrapped && d->elideMode != QQuickText::ElideRight && !scaleFont && !d->isLineLaidOutConnected())
2083 goto geomChangeDone; // only height changed and no multiline eliding.
2085 if (leftAligned && d->elideMode == QQuickText::ElideRight && !d->truncated && d->singleline
2086 && !wrapped && newGeometry.width() > oldGeometry.width() && !scaleFont)
2087 goto geomChangeDone; // Eliding not affected if we're not currently truncated and we get wider.
2089 if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
2091 goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
2092 if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount())
2093 goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
2096 if (d->updateOnComponentComplete || d->textHasChanged) {
2097 // We need to re-elide
2100 // We just need to re-layout
2105 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2108 void QQuickText::triggerPreprocess()
2111 if (d->updateType == QQuickTextPrivate::UpdateNone)
2112 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2116 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2121 if (d->text.isEmpty()) {
2126 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2127 // Update done in preprocess() in the nodes
2128 d->updateType = QQuickTextPrivate::UpdateNone;
2132 d->updateType = QQuickTextPrivate::UpdateNone;
2134 QRectF bounds = boundingRect();
2136 // We need to make sure the layout is done in the current thread
2137 #if defined(Q_OS_MAC)
2138 d->paintingThread = QThread::currentThread();
2139 if (d->layoutThread != d->paintingThread)
2143 QQuickTextNode *node = 0;
2145 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2147 node = static_cast<QQuickTextNode *>(oldNode);
2150 node->deleteContent();
2151 node->setMatrix(QMatrix4x4());
2153 const QColor color = QColor::fromRgba(d->color);
2154 const QColor styleColor = QColor::fromRgba(d->styleColor);
2155 const QColor linkColor = QColor::fromRgba(d->linkColor);
2159 node->addTextDocument(bounds.topLeft(), d->extra->doc, color, d->style, styleColor, linkColor);
2160 } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
2161 int unelidedLineCount = d->lineCount;
2163 unelidedLineCount -= 1;
2164 if (unelidedLineCount > 0) {
2165 node->addTextLayout(
2166 QPoint(0, bounds.y()),
2168 color, d->style, styleColor, linkColor,
2169 QColor(), QColor(), -1, -1,
2170 0, unelidedLineCount);
2173 node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, color, d->style, styleColor, linkColor);
2176 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
2177 QQuickPixmap *pix = img->pix;
2178 if (pix && pix->isReady())
2179 node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
2184 void QQuickText::updatePolish()
2191 \qmlproperty real QtQuick2::Text::contentWidth
2193 Returns the width of the text, including width past the width
2194 which is covered due to insufficient wrapping if WrapMode is set.
2196 qreal QQuickText::contentWidth() const
2198 Q_D(const QQuickText);
2199 return d->layedOutTextRect.width();
2203 \qmlproperty real QtQuick2::Text::contentHeight
2205 Returns the height of the text, including height past the height
2206 which is covered due to there being more text than fits in the set height.
2208 qreal QQuickText::contentHeight() const
2210 Q_D(const QQuickText);
2211 return d->layedOutTextRect.height();
2215 \qmlproperty real QtQuick2::Text::lineHeight
2217 Sets the line height for the text.
2218 The value can be in pixels or a multiplier depending on lineHeightMode.
2220 The default value is a multiplier of 1.0.
2221 The line height must be a positive value.
2223 qreal QQuickText::lineHeight() const
2225 Q_D(const QQuickText);
2226 return d->lineHeight();
2229 void QQuickText::setLineHeight(qreal lineHeight)
2233 if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
2236 d->extra.value().lineHeight = lineHeight;
2238 emit lineHeightChanged(lineHeight);
2242 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2244 This property determines how the line height is specified.
2245 The possible values are:
2248 \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
2249 line (as a multiplier). For example, set to 2 for double spacing.
2250 \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2253 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2255 Q_D(const QQuickText);
2256 return d->lineHeightMode();
2259 void QQuickText::setLineHeightMode(LineHeightMode mode)
2262 if (mode == d->lineHeightMode())
2265 d->extra.value().lineHeightMode = mode;
2268 emit lineHeightModeChanged(mode);
2272 \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2274 This property specifies how the font size of the displayed text is determined.
2275 The possible values are:
2278 \li Text.FixedSize (default) - The size specified by \l font.pixelSize
2279 or \l font.pointSize is used.
2280 \li Text.HorizontalFit - The largest size up to the size specified that fits
2281 within the width of the item without wrapping is used.
2282 \li Text.VerticalFit - The largest size up to the size specified that fits
2283 the height of the item is used.
2284 \li Text.Fit - The largest size up to the size specified the fits within the
2285 width and height of the item is used.
2288 The font size of fitted text has a minimum bound specified by the
2289 minimumPointSize or minimumPixelSize property and maximum bound specified
2290 by either the \l font.pointSize or \l font.pixelSize properties.
2292 If the text does not fit within the item bounds with the minimum font size
2293 the text will be elided as per the \l elide property.
2296 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2298 Q_D(const QQuickText);
2299 return d->fontSizeMode();
2302 void QQuickText::setFontSizeMode(FontSizeMode mode)
2305 if (d->fontSizeMode() == mode)
2310 d->extra.value().fontSizeMode = mode;
2311 emit fontSizeModeChanged();
2315 \qmlproperty int QtQuick2::Text::minimumPixelSize
2317 This property specifies the minimum font pixel size of text scaled by the
2318 fontSizeMode property.
2320 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2321 property is ignored.
2324 int QQuickText::minimumPixelSize() const
2326 Q_D(const QQuickText);
2327 return d->minimumPixelSize();
2330 void QQuickText::setMinimumPixelSize(int size)
2333 if (d->minimumPixelSize() == size)
2336 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2338 d->extra.value().minimumPixelSize = size;
2339 emit minimumPixelSizeChanged();
2343 \qmlproperty int QtQuick2::Text::minimumPointSize
2345 This property specifies the minimum font point \l size of text scaled by
2346 the fontSizeMode property.
2348 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2349 property is ignored.
2352 int QQuickText::minimumPointSize() const
2354 Q_D(const QQuickText);
2355 return d->minimumPointSize();
2358 void QQuickText::setMinimumPointSize(int size)
2361 if (d->minimumPointSize() == size)
2364 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2366 d->extra.value().minimumPointSize = size;
2367 emit minimumPointSizeChanged();
2371 Returns the number of resources (images) that are being loaded asynchronously.
2373 int QQuickText::resourcesLoading() const
2375 Q_D(const QQuickText);
2376 if (d->richText && d->extra.isAllocated() && d->extra->doc)
2377 return d->extra->doc->resourcesLoading();
2382 void QQuickText::componentComplete()
2385 if (d->updateOnComponentComplete) {
2388 d->extra->doc->setText(d->text);
2389 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
2391 d->rightToLeftText = d->text.isRightToLeft();
2393 d->determineHorizontalAlignment();
2395 QQuickItem::componentComplete();
2396 if (d->updateOnComponentComplete)
2401 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
2404 for (int i = 0; i < layout.lineCount(); ++i) {
2405 QTextLine line = layout.lineAt(i);
2406 if (line.naturalTextRect().contains(mousePos)) {
2407 int charPos = line.xToCursor(mousePos.x());
2408 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
2409 if (formatRange.format.isAnchor()
2410 && charPos >= formatRange.start
2411 && charPos <= formatRange.start + formatRange.length) {
2412 return formatRange.format.anchorHref();
2422 bool QQuickTextPrivate::isLinkActivatedConnected()
2425 IS_SIGNAL_CONNECTED(q, "linkActivated(QString)");
2429 void QQuickText::mousePressEvent(QMouseEvent *event)
2434 if (d->isLinkActivatedConnected()) {
2436 link = d->anchorAt(event->localPos());
2437 else if (d->richText) {
2439 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2443 if (link.isEmpty()) {
2444 event->setAccepted(false);
2446 d->extra.value().activeLink = link;
2449 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2451 if (!event->isAccepted())
2452 QQuickItem::mousePressEvent(event);
2457 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2461 // ### confirm the link, and send a signal out
2464 if (d->isLinkActivatedConnected()) {
2466 link = d->anchorAt(event->localPos());
2467 else if (d->richText) {
2469 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2473 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2474 emit linkActivated(d->extra->activeLink);
2476 event->setAccepted(false);
2478 if (!event->isAccepted())
2479 QQuickItem::mouseReleaseEvent(event);