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() : FLT_MAX;
920 if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
924 // If the next needs to be elided and there's an abbreviated string available
925 // go back and do another layout with the abbreviated string.
926 if (eos != -1 && elide) {
928 eos = text.indexOf(QLatin1Char('\x9c'), start);
929 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
930 layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
931 layout.setText(layoutText);
932 textHasChanged = true;
936 if (!horizontalFit && !verticalFit)
939 // Try and find a font size that better fits the dimensions of the element.
940 QRectF unelidedRect = br.united(line.naturalTextRect());
943 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
944 largeFont = scaledFontSize - 1;
945 if (smallFont > largeFont)
947 scaledFontSize = (smallFont + largeFont) / 2;
949 scaledFont.setPixelSize(scaledFontSize);
951 scaledFont.setPointSize(scaledFontSize);
953 } else if (!verticalFit) {
954 smallFont = scaledFontSize;
955 if (smallFont == largeFont)
957 scaledFontSize = (smallFont + largeFont + 1) / 2;
962 if (truncateHeight || unelidedRect.height() > maxHeight) {
963 largeFont = scaledFontSize - 1;
964 if (smallFont > largeFont)
966 scaledFontSize = (smallFont + largeFont) / 2;
969 smallFont = scaledFontSize;
970 if (smallFont == largeFont)
972 scaledFontSize = (smallFont + largeFont + 1) / 2;
977 if (eos != multilengthEos)
982 elideLayout = new QTextLayout;
983 elideLayout->setCacheEnabled(true);
986 QList<QTextLayout::FormatRange> formats;
988 case QQuickText::ElideRight:
989 elideFormats(elideStart, elideText.length() - 1, 0, &formats);
991 case QQuickText::ElideLeft:
992 elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
994 case QQuickText::ElideMiddle: {
995 const int index = elideText.indexOf(elideChar);
997 elideFormats(elideStart, index, 0, &formats);
999 elideEnd - elideText.length() + index + 1,
1000 elideText.length() - index - 1,
1009 elideLayout->setAdditionalFormats(formats);
1012 elideLayout->setFont(layout.font());
1013 elideLayout->setTextOption(layout.textOption());
1014 elideLayout->setText(elideText);
1015 elideLayout->beginLayout();
1017 QTextLine elidedLine = elideLayout->createLine();
1018 elidedLine.setPosition(QPointF(0, height));
1020 setupCustomLineGeometry(elidedLine, height, line.lineNumber());
1022 setLineGeometry(elidedLine, lineWidth, height);
1024 elideLayout->endLayout();
1026 br = br.united(elidedLine.naturalTextRect());
1028 if (visibleCount == 1)
1029 layout.clearLayout();
1035 QTextLine firstLine = visibleCount == 1 && elideLayout
1036 ? elideLayout->lineAt(0)
1038 Q_ASSERT(firstLine.isValid());
1039 *baseline = firstLine.y() + firstLine.ascent();
1042 br.setHeight(height);
1044 //Update the number of visible lines
1045 if (lineCount != visibleCount) {
1046 lineCount = visibleCount;
1047 emit q->lineCountChanged();
1050 if (truncated != wasTruncated)
1051 emit q->truncatedChanged();
1056 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
1059 line.setLineWidth(lineWidth);
1061 if (imgTags.isEmpty()) {
1062 line.setPosition(QPointF(line.position().x(), height));
1063 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
1068 qreal textHeight = line.height();
1069 qreal totalLineHeight = textHeight;
1071 QList<QQuickStyledTextImgTag *> imagesInLine;
1073 foreach (QQuickStyledTextImgTag *image, imgTags) {
1074 if (image->position >= line.textStart() &&
1075 image->position < line.textStart() + line.textLength()) {
1078 QUrl url = q->baseUrl().resolved(image->url);
1079 image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
1080 if (image->pix->isLoading()) {
1081 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
1082 if (!extra.isAllocated() || !extra->nbActiveDownloads)
1083 extra.value().nbActiveDownloads = 0;
1084 extra->nbActiveDownloads++;
1085 } else if (image->pix->isReady()) {
1086 if (!image->size.isValid()) {
1087 image->size = image->pix->implicitSize();
1088 // if the size of the image was not explicitly set, we need to
1089 // call updateLayout() once again.
1090 needToUpdateLayout = true;
1092 } else if (image->pix->isError()) {
1093 qmlInfo(q) << image->pix->error();
1097 qreal ih = qreal(image->size.height());
1098 if (image->align == QQuickStyledTextImgTag::Top)
1100 else if (image->align == QQuickStyledTextImgTag::Middle)
1101 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
1103 image->pos.setY(textHeight - ih);
1104 imagesInLine << image;
1105 textTop = qMax(textTop, qAbs(image->pos.y()));
1109 foreach (QQuickStyledTextImgTag *image, imagesInLine) {
1110 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
1111 image->pos.setX(line.cursorToX(image->position));
1112 image->pos.setY(image->pos.y() + height + textTop);
1113 visibleImgTags << image;
1116 line.setPosition(QPointF(line.position().x(), height + textTop));
1117 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
1121 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1123 void QQuickTextPrivate::ensureDoc()
1125 if (!extra.isAllocated() || !extra->doc) {
1127 extra.value().doc = new QQuickTextDocumentWithImageResources(q);
1128 extra->doc->setDocumentMargin(0);
1129 extra->doc->setBaseUrl(q->baseUrl());
1130 FAST_CONNECT(extra->doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
1135 \qmlclass Text QQuickText
1136 \inqmlmodule QtQuick 2
1137 \ingroup qml-basic-visual-elements
1138 \brief The Text item allows you to add formatted text to a scene.
1141 Text items can display both plain and rich text. For example, red text with
1142 a specific font and size can be defined like this:
1146 text: "Hello World!"
1147 font.family: "Helvetica"
1153 Rich text is defined using HTML-style markup:
1157 text: "<b>Hello</b> <i>World!</i>"
1161 \image declarative-text.png
1163 If height and width are not explicitly set, Text will attempt to determine how
1164 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1165 prefer width to height (all text will be placed on a single line).
1167 The \l elide property can alternatively be used to fit a single line of
1168 plain text to a set width.
1170 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1171 HTML img tags that load remote images, the text is reloaded.
1173 Text provides read-only text. For editable text, see \l TextEdit.
1175 \sa {declarative/text/fonts}{Fonts example}
1177 QQuickText::QQuickText(QQuickItem *parent)
1178 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1184 QQuickText::~QQuickText()
1189 \qmlproperty bool QtQuick2::Text::clip
1190 This property holds whether the text is clipped.
1192 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1194 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1198 \qmlproperty bool QtQuick2::Text::smooth
1200 This property holds whether the text is smoothly scaled or transformed.
1202 Smooth filtering gives better visual quality, but is slower. If
1203 the item is displayed at its natural size, this property has no visual or
1206 \note Generally scaling artifacts are only visible if the item is stationary on
1207 the screen. A common pattern when animating an item is to disable smooth
1208 filtering at the beginning of the animation and reenable it at the conclusion.
1212 \qmlsignal QtQuick2::Text::onLineLaidOut(line)
1214 This handler is called for every line during the layout process.
1215 This gives the opportunity to position and resize a line as it is being laid out.
1216 It can for example be used to create columns or lay out text around objects.
1218 The properties of a line are:
1220 \li number (read-only)
1227 For example, this will move the first 5 lines of a text element by 100 pixels to the right:
1230 if (line.number < 5) {
1231 line.x = line.x + 100
1232 line.width = line.width - 100
1239 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1241 This handler is called when the user clicks on a link embedded in the text.
1242 The link must be in rich text or HTML format and the
1243 \a link string provides access to the particular link.
1245 \snippet doc/src/snippets/qml/text/onLinkActivated.qml 0
1247 The example code will display the text
1248 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1250 Clicking on the highlighted link will output
1251 \tt{http://qt.nokia.com link activated} to the console.
1255 \qmlproperty string QtQuick2::Text::font.family
1257 Sets the family name of the font.
1259 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1260 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1261 If the family isn't available a family will be set using the font matching algorithm.
1265 \qmlproperty bool QtQuick2::Text::font.bold
1267 Sets whether the font weight is bold.
1271 \qmlproperty enumeration QtQuick2::Text::font.weight
1273 Sets the font's weight.
1275 The weight can be one of:
1278 \li Font.Normal - the default
1285 Text { text: "Hello"; font.weight: Font.DemiBold }
1290 \qmlproperty bool QtQuick2::Text::font.italic
1292 Sets whether the font has an italic style.
1296 \qmlproperty bool QtQuick2::Text::font.underline
1298 Sets whether the text is underlined.
1302 \qmlproperty bool QtQuick2::Text::font.strikeout
1304 Sets whether the font has a strikeout style.
1308 \qmlproperty real QtQuick2::Text::font.pointSize
1310 Sets the font size in points. The point size must be greater than zero.
1314 \qmlproperty int QtQuick2::Text::font.pixelSize
1316 Sets the font size in pixels.
1318 Using this function makes the font device dependent.
1319 Use \c pointSize to set the size of the font in a device independent manner.
1323 \qmlproperty real QtQuick2::Text::font.letterSpacing
1325 Sets the letter spacing for the font.
1327 Letter spacing changes the default spacing between individual letters in the font.
1328 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1332 \qmlproperty real QtQuick2::Text::font.wordSpacing
1334 Sets the word spacing for the font.
1336 Word spacing changes the default spacing between individual words.
1337 A positive value increases the word spacing by a corresponding amount of pixels,
1338 while a negative value decreases the inter-word spacing accordingly.
1342 \qmlproperty enumeration QtQuick2::Text::font.capitalization
1344 Sets the capitalization for the text.
1347 \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1348 \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1349 \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1350 \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
1351 \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1355 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1358 QFont QQuickText::font() const
1360 Q_D(const QQuickText);
1361 return d->sourceFont;
1364 void QQuickText::setFont(const QFont &font)
1367 if (d->sourceFont == font)
1370 d->sourceFont = font;
1371 QFont oldFont = d->font;
1374 if (d->font.pointSizeF() != -1) {
1376 qreal size = qRound(d->font.pointSizeF()*2.0);
1377 d->font.setPointSizeF(size/2.0);
1380 if (oldFont != d->font) {
1381 // if the format changes the size of the text
1382 // with headings or <font> tag, we need to re-parse
1383 if (d->formatModifiesFontSize)
1384 d->textHasChanged = true;
1388 emit fontChanged(d->sourceFont);
1392 \qmlproperty string QtQuick2::Text::text
1394 The text to display. Text supports both plain and rich text strings.
1396 The item will try to automatically determine whether the text should
1397 be treated as styled text. This determination is made using Qt::mightBeRichText().
1399 QString QQuickText::text() const
1401 Q_D(const QQuickText);
1405 void QQuickText::setText(const QString &n)
1411 d->richText = d->format == RichText;
1412 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1414 if (isComponentComplete()) {
1417 d->extra->doc->setText(n);
1418 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1420 d->rightToLeftText = d->text.isRightToLeft();
1422 d->determineHorizontalAlignment();
1424 d->textHasChanged = true;
1425 qDeleteAll(d->imgTags);
1428 emit textChanged(d->text);
1432 \qmlproperty color QtQuick2::Text::color
1436 An example of green text defined using hexadecimal notation:
1444 An example of steel blue text defined using an SVG color name:
1452 QColor QQuickText::color() const
1454 Q_D(const QQuickText);
1455 return QColor::fromRgba(d->color);
1458 void QQuickText::setColor(const QColor &color)
1461 QRgb rgb = color.rgba();
1462 if (d->color == rgb)
1466 if (isComponentComplete()) {
1467 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1470 emit colorChanged();
1474 \qmlproperty color QtQuick2::Text::linkColor
1476 The color of links in the text.
1478 This property works with the StyledText \l textFormat, but not with RichText.
1479 Link color in RichText can be specified by including CSS style tags in the
1483 QColor QQuickText::linkColor() const
1485 Q_D(const QQuickText);
1486 return QColor::fromRgba(d->linkColor);
1489 void QQuickText::setLinkColor(const QColor &color)
1492 QRgb rgb = color.rgba();
1493 if (d->linkColor == rgb)
1498 emit linkColorChanged();
1502 \qmlproperty enumeration QtQuick2::Text::style
1504 Set an additional text style.
1506 Supported text styles are:
1508 \li Text.Normal - the default
1516 Text { font.pointSize: 24; text: "Normal" }
1517 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1518 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1519 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1523 \image declarative-textstyle.png
1525 QQuickText::TextStyle QQuickText::style() const
1527 Q_D(const QQuickText);
1531 void QQuickText::setStyle(QQuickText::TextStyle style)
1534 if (d->style == style)
1538 if (isComponentComplete()) {
1539 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1542 emit styleChanged(d->style);
1546 \qmlproperty color QtQuick2::Text::styleColor
1548 Defines the secondary color used by text styles.
1550 \c styleColor is used as the outline color for outlined text, and as the
1551 shadow color for raised or sunken text. If no style has been set, it is not
1555 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1560 QColor QQuickText::styleColor() const
1562 Q_D(const QQuickText);
1563 return QColor::fromRgba(d->styleColor);
1566 void QQuickText::setStyleColor(const QColor &color)
1569 QRgb rgb = color.rgba();
1570 if (d->styleColor == rgb)
1573 d->styleColor = rgb;
1574 if (isComponentComplete()) {
1575 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1578 emit styleColorChanged();
1582 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1583 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1584 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1586 Sets the horizontal and vertical alignment of the text within the Text items
1587 width and height. By default, the text is vertically aligned to the top. Horizontal
1588 alignment follows the natural alignment of the text, for example text that is read
1589 from left to right will be aligned to the left.
1591 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1592 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1593 and \c Text.AlignVCenter.
1595 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1596 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1597 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1600 When using the attached property LayoutMirroring::enabled to mirror application
1601 layouts, the horizontal alignment of text will also be mirrored. However, the property
1602 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1603 of Text, use the read-only property \c effectiveHorizontalAlignment.
1605 QQuickText::HAlignment QQuickText::hAlign() const
1607 Q_D(const QQuickText);
1611 void QQuickText::setHAlign(HAlignment align)
1614 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1615 d->hAlignImplicit = false;
1616 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1620 void QQuickText::resetHAlign()
1623 d->hAlignImplicit = true;
1624 if (isComponentComplete() && d->determineHorizontalAlignment())
1628 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1630 Q_D(const QQuickText);
1631 QQuickText::HAlignment effectiveAlignment = d->hAlign;
1632 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1633 switch (d->hAlign) {
1634 case QQuickText::AlignLeft:
1635 effectiveAlignment = QQuickText::AlignRight;
1637 case QQuickText::AlignRight:
1638 effectiveAlignment = QQuickText::AlignLeft;
1644 return effectiveAlignment;
1647 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1650 if (hAlign != alignment || forceAlign) {
1651 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1654 emit q->horizontalAlignmentChanged(hAlign);
1655 if (oldEffectiveHAlign != q->effectiveHAlign())
1656 emit q->effectiveHorizontalAlignmentChanged();
1662 bool QQuickTextPrivate::determineHorizontalAlignment()
1664 if (hAlignImplicit) {
1665 bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1666 return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1671 void QQuickTextPrivate::mirrorChange()
1674 if (q->isComponentComplete()) {
1675 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1677 emit q->effectiveHorizontalAlignmentChanged();
1682 QQuickText::VAlignment QQuickText::vAlign() const
1684 Q_D(const QQuickText);
1688 void QQuickText::setVAlign(VAlignment align)
1691 if (d->vAlign == align)
1695 emit verticalAlignmentChanged(align);
1699 \qmlproperty enumeration QtQuick2::Text::wrapMode
1701 Set this property to wrap the text to the Text item's width. The text will only
1702 wrap if an explicit width has been set. wrapMode can be one of:
1705 \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
1706 \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
1707 \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1708 \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.
1711 QQuickText::WrapMode QQuickText::wrapMode() const
1713 Q_D(const QQuickText);
1717 void QQuickText::setWrapMode(WrapMode mode)
1720 if (mode == d->wrapMode)
1726 emit wrapModeChanged();
1730 \qmlproperty int QtQuick2::Text::lineCount
1732 Returns the number of lines visible in the text item.
1734 This property is not supported for rich text.
1736 \sa maximumLineCount
1738 int QQuickText::lineCount() const
1740 Q_D(const QQuickText);
1741 return d->lineCount;
1745 \qmlproperty bool QtQuick2::Text::truncated
1747 Returns true if the text has been truncated due to \l maximumLineCount
1750 This property is not supported for rich text.
1752 \sa maximumLineCount, elide
1754 bool QQuickText::truncated() const
1756 Q_D(const QQuickText);
1757 return d->truncated;
1761 \qmlproperty int QtQuick2::Text::maximumLineCount
1763 Set this property to limit the number of lines that the text item will show.
1764 If elide is set to Text.ElideRight, the text will be elided appropriately.
1765 By default, this is the value of the largest possible integer.
1767 This property is not supported for rich text.
1769 \sa lineCount, elide
1771 int QQuickText::maximumLineCount() const
1773 Q_D(const QQuickText);
1774 return d->maximumLineCount();
1777 void QQuickText::setMaximumLineCount(int lines)
1781 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1782 if (d->maximumLineCount() != lines) {
1783 d->extra.value().maximumLineCount = lines;
1785 emit maximumLineCountChanged();
1789 void QQuickText::resetMaximumLineCount()
1792 setMaximumLineCount(INT_MAX);
1793 if (d->truncated != false) {
1794 d->truncated = false;
1795 emit truncatedChanged();
1800 \qmlproperty enumeration QtQuick2::Text::textFormat
1802 The way the text property should be displayed.
1804 Supported text formats are:
1807 \li Text.AutoText (default)
1813 If the text format is \c Text.AutoText the text element
1814 will automatically determine whether the text should be treated as
1815 styled text. This determination is made using Qt::mightBeRichText()
1816 which uses a fast and therefore simple heuristic. It mainly checks
1817 whether there is something that looks like a tag before the first
1818 line break. Although the result may be correct for common cases,
1819 there is no guarantee.
1821 Text.StyledText is an optimized format supporting some basic text
1822 styling markup, in the style of html 3.2:
1826 <strong></strong> - bold
1830 <u> - underlined text
1831 <font color="color_name" size="1-7"></font>
1832 <h1> to <h6> - headers
1833 <a href=""> - anchor
1834 <img src="" align="top,middle,bottom" width="" height=""> - inline images
1835 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1836 <pre></pre> - preformatted
1840 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1849 text: "<b>Hello</b> <i>World!</i>"
1853 textFormat: Text.RichText
1854 text: "<b>Hello</b> <i>World!</i>"
1858 textFormat: Text.PlainText
1859 text: "<b>Hello</b> <i>World!</i>"
1863 \li \image declarative-textformat.png
1866 QQuickText::TextFormat QQuickText::textFormat() const
1868 Q_D(const QQuickText);
1872 void QQuickText::setTextFormat(TextFormat format)
1875 if (format == d->format)
1878 bool wasRich = d->richText;
1879 d->richText = format == RichText;
1880 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1882 if (isComponentComplete()) {
1883 if (!wasRich && d->richText) {
1885 d->extra->doc->setText(d->text);
1886 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1888 d->rightToLeftText = d->text.isRightToLeft();
1890 d->determineHorizontalAlignment();
1894 emit textFormatChanged(d->format);
1898 \qmlproperty enumeration QtQuick2::Text::elide
1900 Set this property to elide parts of the text fit to the Text item's width.
1901 The text will only elide if an explicit width has been set.
1903 This property cannot be used with rich text.
1907 \li Text.ElideNone - the default
1909 \li Text.ElideMiddle
1913 If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
1914 text. The text will only elide if \c maximumLineCount, or \c height has been set.
1915 If both \c maximumLineCount and \c height are set, \c maximumLineCount will
1916 apply unless the lines do not fit in the height allowed.
1918 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
1919 the first string that fits will be used, otherwise the last will be elided.
1921 Multi-length strings are ordered from longest to shortest, separated by the
1922 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
1924 QQuickText::TextElideMode QQuickText::elideMode() const
1926 Q_D(const QQuickText);
1927 return d->elideMode;
1930 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
1933 if (mode == d->elideMode)
1936 d->elideMode = mode;
1939 emit elideModeChanged(mode);
1943 \qmlproperty url QtQuick2::Text::baseUrl
1945 This property specifies a base URL which is used to resolve relative URLs
1948 Urls are resolved to be within the same directory as the target of the base
1949 URL meaning any portion of the path after the last '/' will be ignored.
1952 \header \li Base URL \li Relative URL \li Resolved URL
1953 \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
1954 \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
1955 \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
1956 \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
1957 \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
1958 \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
1959 \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
1962 By default is the url of the Text element.
1965 QUrl QQuickText::baseUrl() const
1967 Q_D(const QQuickText);
1968 if (d->baseUrl.isEmpty()) {
1969 if (QQmlContext *context = qmlContext(this))
1970 const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
1975 void QQuickText::setBaseUrl(const QUrl &url)
1978 if (baseUrl() != url) {
1983 d->extra->doc->setBaseUrl(url);
1985 if (d->styledText) {
1986 d->textHasChanged = true;
1987 qDeleteAll(d->imgTags);
1991 emit baseUrlChanged();
1995 void QQuickText::resetBaseUrl()
1997 if (QQmlContext *context = qmlContext(this))
1998 setBaseUrl(context->baseUrl());
2004 QRectF QQuickText::boundingRect() const
2006 Q_D(const QQuickText);
2008 QRectF rect = d->layedOutTextRect;
2009 if (d->style != Normal)
2010 rect.adjust(-1, 0, 1, 2);
2012 // Could include font max left/right bearings to either side of rectangle.
2015 switch (d->hAlign) {
2020 rect.moveLeft(w - rect.width());
2023 rect.moveLeft((w - rect.width()) / 2);
2028 switch (d->vAlign) {
2032 rect.moveTop(h - rect.height());
2035 rect.moveTop((h - rect.height()) / 2);
2043 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2046 if (d->text.isEmpty()) {
2047 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2051 bool widthChanged = newGeometry.width() != oldGeometry.width();
2052 bool heightChanged = newGeometry.height() != oldGeometry.height();
2053 bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
2054 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2055 bool elide = d->elideMode != QQuickText::ElideNone;
2056 bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
2058 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2059 goto geomChangeDone;
2061 if (leftAligned && !wrapped && !elide && !scaleFont)
2062 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2064 if (!widthChanged && !wrapped && d->singleline && !scaleFont)
2065 goto geomChangeDone; // only height has changed which doesn't affect single line unwrapped text
2067 if (!widthChanged && wrapped && d->elideMode != QQuickText::ElideRight && !scaleFont)
2068 goto geomChangeDone; // only height changed and no multiline eliding.
2070 if (leftAligned && d->elideMode == QQuickText::ElideRight && !d->truncated && d->singleline
2071 && !wrapped && newGeometry.width() > oldGeometry.width() && !scaleFont)
2072 goto geomChangeDone; // Eliding not affected if we're not currently truncated and we get wider.
2074 if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
2076 goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
2077 if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount())
2078 goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
2081 if (d->updateOnComponentComplete || d->textHasChanged) {
2082 // We need to re-elide
2085 // We just need to re-layout
2090 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2093 void QQuickText::triggerPreprocess()
2096 if (d->updateType == QQuickTextPrivate::UpdateNone)
2097 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2101 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2106 if (d->text.isEmpty()) {
2111 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2112 // Update done in preprocess() in the nodes
2113 d->updateType = QQuickTextPrivate::UpdateNone;
2117 d->updateType = QQuickTextPrivate::UpdateNone;
2119 QRectF bounds = boundingRect();
2121 // We need to make sure the layout is done in the current thread
2122 #if defined(Q_OS_MAC)
2123 d->paintingThread = QThread::currentThread();
2124 if (d->layoutThread != d->paintingThread)
2128 QQuickTextNode *node = 0;
2130 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2132 node = static_cast<QQuickTextNode *>(oldNode);
2135 node->deleteContent();
2136 node->setMatrix(QMatrix4x4());
2138 const QColor color = QColor::fromRgba(d->color);
2139 const QColor styleColor = QColor::fromRgba(d->styleColor);
2140 const QColor linkColor = QColor::fromRgba(d->linkColor);
2144 node->addTextDocument(bounds.topLeft(), d->extra->doc, color, d->style, styleColor, linkColor);
2145 } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
2146 int unelidedLineCount = d->lineCount;
2148 unelidedLineCount -= 1;
2149 if (unelidedLineCount > 0) {
2150 node->addTextLayout(
2151 QPoint(0, bounds.y()),
2153 d->color, d->style, d->styleColor, d->linkColor,
2154 QColor(), QColor(), -1, -1,
2155 0, unelidedLineCount);
2158 node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, color, d->style, styleColor, linkColor);
2161 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
2162 QQuickPixmap *pix = img->pix;
2163 if (pix && pix->isReady())
2164 node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
2169 void QQuickText::updatePolish()
2176 \qmlproperty real QtQuick2::Text::contentWidth
2178 Returns the width of the text, including width past the width
2179 which is covered due to insufficient wrapping if WrapMode is set.
2181 qreal QQuickText::contentWidth() const
2183 Q_D(const QQuickText);
2184 return d->layedOutTextRect.width();
2188 \qmlproperty real QtQuick2::Text::contentHeight
2190 Returns the height of the text, including height past the height
2191 which is covered due to there being more text than fits in the set height.
2193 qreal QQuickText::contentHeight() const
2195 Q_D(const QQuickText);
2196 return d->layedOutTextRect.height();
2200 \qmlproperty real QtQuick2::Text::lineHeight
2202 Sets the line height for the text.
2203 The value can be in pixels or a multiplier depending on lineHeightMode.
2205 The default value is a multiplier of 1.0.
2206 The line height must be a positive value.
2208 qreal QQuickText::lineHeight() const
2210 Q_D(const QQuickText);
2211 return d->lineHeight();
2214 void QQuickText::setLineHeight(qreal lineHeight)
2218 if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
2221 d->extra.value().lineHeight = lineHeight;
2223 emit lineHeightChanged(lineHeight);
2227 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2229 This property determines how the line height is specified.
2230 The possible values are:
2233 \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
2234 line (as a multiplier). For example, set to 2 for double spacing.
2235 \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2238 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2240 Q_D(const QQuickText);
2241 return d->lineHeightMode();
2244 void QQuickText::setLineHeightMode(LineHeightMode mode)
2247 if (mode == d->lineHeightMode())
2250 d->extra.value().lineHeightMode = mode;
2253 emit lineHeightModeChanged(mode);
2257 \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2259 This property specifies how the font size of the displayed text is determined.
2260 The possible values are:
2263 \li Text.FixedSize (default) - The size specified by \l font.pixelSize
2264 or \l font.pointSize is used.
2265 \li Text.HorizontalFit - The largest size up to the size specified that fits
2266 within the width of the item without wrapping is used.
2267 \li Text.VerticalFit - The largest size up to the size specified that fits
2268 the height of the item is used.
2269 \li Text.Fit - The largest size up to the size specified the fits within the
2270 width and height of the item is used.
2273 The font size of fitted text has a minimum bound specified by the
2274 minimumPointSize or minimumPixelSize property and maximum bound specified
2275 by either the \l font.pointSize or \l font.pixelSize properties.
2277 If the text does not fit within the item bounds with the minimum font size
2278 the text will be elided as per the \l elide property.
2281 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2283 Q_D(const QQuickText);
2284 return d->fontSizeMode();
2287 void QQuickText::setFontSizeMode(FontSizeMode mode)
2290 if (d->fontSizeMode() == mode)
2295 d->extra.value().fontSizeMode = mode;
2296 emit fontSizeModeChanged();
2300 \qmlproperty int QtQuick2::Text::minimumPixelSize
2302 This property specifies the minimum font pixel size of text scaled by the
2303 fontSizeMode property.
2305 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2306 property is ignored.
2309 int QQuickText::minimumPixelSize() const
2311 Q_D(const QQuickText);
2312 return d->minimumPixelSize();
2315 void QQuickText::setMinimumPixelSize(int size)
2318 if (d->minimumPixelSize() == size)
2321 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2323 d->extra.value().minimumPixelSize = size;
2324 emit minimumPixelSizeChanged();
2328 \qmlproperty int QtQuick2::Text::minimumPointSize
2330 This property specifies the minimum font point \l size of text scaled by
2331 the fontSizeMode property.
2333 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2334 property is ignored.
2337 int QQuickText::minimumPointSize() const
2339 Q_D(const QQuickText);
2340 return d->minimumPointSize();
2343 void QQuickText::setMinimumPointSize(int size)
2346 if (d->minimumPointSize() == size)
2349 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2351 d->extra.value().minimumPointSize = size;
2352 emit minimumPointSizeChanged();
2356 Returns the number of resources (images) that are being loaded asynchronously.
2358 int QQuickText::resourcesLoading() const
2360 Q_D(const QQuickText);
2361 if (d->richText && d->extra.isAllocated() && d->extra->doc)
2362 return d->extra->doc->resourcesLoading();
2367 void QQuickText::componentComplete()
2370 if (d->updateOnComponentComplete) {
2373 d->extra->doc->setText(d->text);
2374 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
2376 d->rightToLeftText = d->text.isRightToLeft();
2378 d->determineHorizontalAlignment();
2380 QQuickItem::componentComplete();
2381 if (d->updateOnComponentComplete)
2386 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos)
2389 for (int i = 0; i < layout.lineCount(); ++i) {
2390 QTextLine line = layout.lineAt(i);
2391 if (line.naturalTextRect().contains(mousePos)) {
2392 int charPos = line.xToCursor(mousePos.x());
2393 foreach (const QTextLayout::FormatRange &formatRange, layout.additionalFormats()) {
2394 if (formatRange.format.isAnchor()
2395 && charPos >= formatRange.start
2396 && charPos <= formatRange.start + formatRange.length) {
2397 return formatRange.format.anchorHref();
2407 bool QQuickTextPrivate::isLinkActivatedConnected()
2410 IS_SIGNAL_CONNECTED(q, "linkActivated(QString)");
2414 void QQuickText::mousePressEvent(QMouseEvent *event)
2419 if (d->isLinkActivatedConnected()) {
2421 link = d->anchorAt(event->localPos());
2422 else if (d->richText) {
2424 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2428 if (link.isEmpty()) {
2429 event->setAccepted(false);
2431 d->extra.value().activeLink = link;
2434 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2436 if (!event->isAccepted())
2437 QQuickItem::mousePressEvent(event);
2442 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2446 // ### confirm the link, and send a signal out
2449 if (d->isLinkActivatedConnected()) {
2451 link = d->anchorAt(event->localPos());
2452 else if (d->richText) {
2454 link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
2458 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2459 emit linkActivated(d->extra->activeLink);
2461 event->setAccepted(false);
2463 if (!event->isAccepted())
2464 QQuickItem::mouseReleaseEvent(event);