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 "qquicktextutil_p.h"
52 #include <QtQuick/private/qsgtexture_p.h>
54 #include <QtQml/qqmlinfo.h>
55 #include <QtGui/qevent.h>
56 #include <QtGui/qabstracttextdocumentlayout.h>
57 #include <QtGui/qpainter.h>
58 #include <QtGui/qtextdocument.h>
59 #include <QtGui/qtextobject.h>
60 #include <QtGui/qtextcursor.h>
61 #include <QtGui/qguiapplication.h>
62 #include <QtGui/qinputmethod.h>
64 #include <private/qtextengine_p.h>
65 #include <private/qquickstyledtext_p.h>
66 #include <QtQuick/private/qquickpixmapcache_p.h>
74 const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
76 QQuickTextPrivate::QQuickTextPrivate()
77 : elideLayout(0), textLine(0), lineWidth(0)
78 , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
79 , lineCount(1), multilengthEos(-1)
80 , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
81 , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
82 , style(QQuickText::Normal)
83 , renderType(QQuickText::QtRendering)
84 , updateType(UpdatePaintNode)
85 , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
86 , styledText(false), widthExceeded(false), heightExceeded(false), internalWidthUpdate(false)
87 , requireImplicitSize(false), implicitWidthValid(false), implicitHeightValid(false)
88 , truncated(false), hAlignImplicit(true), rightToLeftText(false)
89 , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
93 QQuickTextPrivate::ExtraData::ExtraData()
96 , minimumPixelSize(12)
97 , minimumPointSize(12)
98 , nbActiveDownloads(0)
99 , maximumLineCount(INT_MAX)
100 , lineHeightMode(QQuickText::ProportionalHeight)
101 , fontSizeMode(QQuickText::FixedSize)
105 void QQuickTextPrivate::init()
108 q->setAcceptedMouseButtons(Qt::LeftButton);
109 q->setFlag(QQuickItem::ItemHasContents);
112 QQuickTextDocumentWithImageResources::QQuickTextDocumentWithImageResources(QQuickItem *parent)
113 : QTextDocument(parent), outstanding(0)
115 setUndoRedoEnabled(false);
116 documentLayout()->registerHandler(QTextFormat::ImageObject, this);
119 QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
121 if (!m_resources.isEmpty())
122 qDeleteAll(m_resources);
125 QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
127 QQmlContext *context = qmlContext(parent());
128 QUrl url = m_baseUrl.resolved(name);
130 if (type == QTextDocument::ImageResource) {
131 QQuickPixmap *p = loadPixmap(context, url);
135 return QTextDocument::loadResource(type,url); // The *resolved* URL
138 void QQuickTextDocumentWithImageResources::requestFinished()
141 if (outstanding == 0) {
142 markContentsDirty(0, characterCount());
147 void QQuickTextDocumentWithImageResources::clear()
151 QTextDocument::clear();
155 QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
156 QTextDocument *, int, const QTextFormat &format)
158 if (format.isImageFormat()) {
159 QTextImageFormat imageFormat = format.toImageFormat();
161 const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
162 const int width = qRound(imageFormat.width());
163 const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
164 const int height = qRound(imageFormat.height());
166 QSizeF size(width, height);
167 if (!hasWidth || !hasHeight) {
168 QQmlContext *context = qmlContext(parent());
169 QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
171 QQuickPixmap *p = loadPixmap(context, url);
179 QSize implicitSize = p->implicitSize();
183 size.setWidth(implicitSize.width());
185 size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
189 size.setHeight(implicitSize.height());
191 size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
199 void QQuickTextDocumentWithImageResources::drawObject(
200 QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
204 QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
206 QQmlContext *context = qmlContext(parent());
207 QUrl url = m_baseUrl.resolved(QUrl(format.name()));
209 QQuickPixmap *p = loadPixmap(context, url);
213 void QQuickTextDocumentWithImageResources::setBaseUrl(const QUrl &url, bool clear)
218 markContentsDirty(0, characterCount());
222 QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
223 QQmlContext *context, const QUrl &url)
226 QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
228 if (iter == m_resources.end()) {
229 QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
230 iter = m_resources.insert(url, p);
232 if (p->isLoading()) {
233 p->connectFinished(this, SLOT(requestFinished()));
238 QQuickPixmap *p = *iter;
240 if (!errors.contains(url)) {
242 qmlInfo(parent()) << p->error();
248 void QQuickTextDocumentWithImageResources::clearResources()
250 foreach (QQuickPixmap *pixmap, m_resources)
252 qDeleteAll(m_resources);
257 void QQuickTextDocumentWithImageResources::setText(const QString &text)
261 #ifndef QT_NO_TEXTHTMLPARSER
268 QSet<QUrl> QQuickTextDocumentWithImageResources::errors;
270 QQuickTextPrivate::~QQuickTextPrivate()
273 delete textLine; textLine = 0;
278 qreal QQuickTextPrivate::getImplicitWidth() const
280 if (!requireImplicitSize) {
281 // We don't calculate implicitWidth unless it is required.
282 // We need to force a size update now to ensure implicitWidth is calculated
283 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
284 me->requireImplicitSize = true;
287 return implicitWidth;
290 qreal QQuickTextPrivate::getImplicitHeight() const
292 if (!requireImplicitSize) {
293 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
294 me->requireImplicitSize = true;
297 return implicitHeight;
301 \qmlproperty enumeration QtQuick2::Text::renderType
303 Override the default rendering type for this component.
305 Supported render types are:
307 \li Text.QtRendering - the default
308 \li Text.NativeRendering
311 Select Text.NativeRendering if you prefer text to look native on the target platform and do
312 not require advanced features such as transformation of the text. Using such features in
313 combination with the NativeRendering render type will lend poor and sometimes pixelated
316 QQuickText::RenderType QQuickText::renderType() const
318 Q_D(const QQuickText);
319 return d->renderType;
322 void QQuickText::setRenderType(QQuickText::RenderType renderType)
325 if (d->renderType == renderType)
328 d->renderType = renderType;
329 emit renderTypeChanged();
331 if (isComponentComplete())
335 void QQuickText::q_imagesLoaded()
341 void QQuickTextPrivate::updateLayout()
344 if (!q->isComponentComplete()) {
345 updateOnComponentComplete = true;
348 updateOnComponentComplete = false;
349 layoutTextElided = false;
351 if (!visibleImgTags.isEmpty())
352 visibleImgTags.clear();
353 needToUpdateLayout = false;
355 // Setup instance of QTextLayout for all cases other than richtext
357 if (textHasChanged) {
358 if (styledText && !text.isEmpty()) {
359 layout.setFont(font);
360 // needs temporary bool because formatModifiesFontSize is in a bit-field
361 bool fontSizeModified = false;
362 QQuickStyledText::parse(text, layout, imgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
363 formatModifiesFontSize = fontSizeModified;
366 layout.clearAdditionalFormats();
368 elideLayout->clearAdditionalFormats();
370 multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
371 if (multilengthEos != -1) {
372 tmp = tmp.mid(0, multilengthEos);
373 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
374 } else if (tmp.contains(QLatin1Char('\n'))) {
375 // Replace always does a detach. Checking for the new line character first
376 // means iterating over those items again if found but prevents a realloc
378 tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
382 textHasChanged = false;
386 QTextBlockFormat::LineHeightTypes type;
387 type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
388 QTextBlockFormat blockFormat;
389 blockFormat.setLineHeight((lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), type);
390 for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
391 QTextCursor cursor(it);
392 cursor.mergeBlockFormat(blockFormat);
398 if (needToUpdateLayout) {
399 needToUpdateLayout = false;
400 textHasChanged = true;
405 void QQuickText::imageDownloadFinished()
409 (d->extra->nbActiveDownloads)--;
411 // when all the remote images have been downloaded,
412 // if one of the sizes was not specified at parsing time
413 // we use the implicit size from pixmapcache and re-layout.
415 if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
416 bool needToUpdateLayout = false;
417 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
418 if (!img->size.isValid()) {
419 img->size = img->pix->implicitSize();
420 needToUpdateLayout = true;
424 if (needToUpdateLayout) {
425 d->textHasChanged = true;
428 d->updateType = QQuickTextPrivate::UpdatePaintNode;
434 void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy)
440 if (q->heightValid()) {
441 if (vAlign == QQuickText::AlignBottom)
443 else if (vAlign == QQuickText::AlignVCenter)
447 q->setBaselineOffset(baseline + yoff);
450 void QQuickTextPrivate::updateSize()
454 if (!q->isComponentComplete()) {
455 updateOnComponentComplete = true;
459 if (!requireImplicitSize) {
460 emit q->implicitWidthChanged();
461 emit q->implicitHeightChanged();
462 // if the implicitWidth is used, then updateSize() has already been called (recursively)
463 if (requireImplicitSize)
467 if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
468 // How much more expensive is it to just do a full layout on an empty string here?
469 // There may be subtle differences in the height and baseline calculations between
470 // QTextLayout and QFontMetrics and the number of variables that can affect the size
471 // and position of a line is increasing.
472 QFontMetricsF fm(font);
473 qreal fontHeight = qCeil(fm.height()); // QScriptLine and therefore QTextLine rounds up
474 if (!richText) { // line height, so we will as well.
475 fontHeight = lineHeightMode() == QQuickText::FixedHeight
477 : fontHeight * lineHeight();
479 updateBaseline(fm.ascent(), q->height() - fontHeight);
480 q->setImplicitSize(0, fontHeight);
481 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
482 emit q->contentSizeChanged();
483 updateType = UpdatePaintNode;
489 QSizeF previousSize = layedOutTextRect.size();
491 //setup instance of QTextLayout for all cases other than richtext
494 QRectF textRect = setupTextLayout(&baseline);
496 if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
497 return; // get this far we'll get a warning to that effect if it is.
499 layedOutTextRect = textRect;
500 size = textRect.size();
501 updateBaseline(baseline, q->height() - size.height());
503 widthExceeded = true; // always relayout rich text on width changes..
504 heightExceeded = false; // rich text layout isn't affected by height changes.
506 extra->doc->setDefaultFont(font);
507 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
508 if (rightToLeftText) {
509 if (horizontalAlignment == QQuickText::AlignLeft)
510 horizontalAlignment = QQuickText::AlignRight;
511 else if (horizontalAlignment == QQuickText::AlignRight)
512 horizontalAlignment = QQuickText::AlignLeft;
515 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
516 option.setWrapMode(QTextOption::WrapMode(wrapMode));
517 option.setUseDesignMetrics(renderType != QQuickText::NativeRendering);
518 extra->doc->setDefaultTextOption(option);
519 qreal naturalWidth = 0;
520 if (requireImplicitSize && q->widthValid()) {
521 extra->doc->setTextWidth(-1);
522 naturalWidth = extra->doc->idealWidth();
523 const bool wasInLayout = internalWidthUpdate;
524 internalWidthUpdate = true;
525 q->setImplicitWidth(naturalWidth);
526 internalWidthUpdate = wasInLayout;
528 if (internalWidthUpdate)
530 if (wrapMode != QQuickText::NoWrap && q->widthValid())
531 extra->doc->setTextWidth(q->width());
533 extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
534 widthExceeded = extra->doc->textWidth() < extra->doc->idealWidth();
535 QSizeF dsize = extra->doc->size();
536 layedOutTextRect = QRectF(QPointF(0,0), dsize);
537 size = QSizeF(extra->doc->idealWidth(),dsize.height());
539 QFontMetricsF fm(font);
540 updateBaseline(fm.ascent(), q->height() - size.height());
542 //### need to confirm cost of always setting these for richText
543 internalWidthUpdate = true;
545 if (!q->widthValid())
546 iWidth = size.width();
548 q->setImplicitSize(iWidth, size.height());
549 internalWidthUpdate = false;
552 q->setImplicitHeight(size.height());
555 if (layedOutTextRect.size() != previousSize)
556 emit q->contentSizeChanged();
557 updateType = UpdatePaintNode;
561 QQuickTextLine::QQuickTextLine()
562 : QObject(), m_line(0), m_height(0)
566 void QQuickTextLine::setLine(QTextLine *line)
571 void QQuickTextLine::setLineOffset(int offset)
573 m_lineOffset = offset;
576 int QQuickTextLine::number() const
579 return m_line->lineNumber() + m_lineOffset;
583 qreal QQuickTextLine::width() const
586 return m_line->width();
590 void QQuickTextLine::setWidth(qreal width)
593 m_line->setLineWidth(width);
596 qreal QQuickTextLine::height() const
601 return m_line->height();
605 void QQuickTextLine::setHeight(qreal height)
608 m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
612 qreal QQuickTextLine::x() const
619 void QQuickTextLine::setX(qreal x)
622 m_line->setPosition(QPointF(x, m_line->y()));
625 qreal QQuickTextLine::y() const
632 void QQuickTextLine::setY(qreal y)
635 m_line->setPosition(QPointF(m_line->x(), y));
639 \qmlmethod QtQuick2::Text::doLayout()
641 Triggers a re-layout of the displayed text.
643 void QQuickText::doLayout()
649 bool QQuickTextPrivate::isLineLaidOutConnected()
652 IS_SIGNAL_CONNECTED(q, QQuickText, lineLaidOut, (QQuickTextLine *));
655 void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
660 textLine = new QQuickTextLine;
661 textLine->setLine(&line);
662 textLine->setY(height);
663 textLine->setHeight(0);
664 textLine->setLineOffset(lineOffset);
666 // use the text item's width by default if it has one and wrap is on
667 if (q->widthValid() && q->wrapMode() != QQuickText::NoWrap)
668 textLine->setWidth(q->width());
670 textLine->setWidth(INT_MAX);
671 if (lineHeight() != 1.0)
672 textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
674 emit q->lineLaidOut(textLine);
676 height += textLine->height();
679 void QQuickTextPrivate::elideFormats(
680 const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
682 const int end = start + length;
683 QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
684 for (int i = 0; i < formats.count(); ++i) {
685 QTextLayout::FormatRange format = formats.at(i);
686 const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
687 if (formatLength > 0) {
688 format.start = qMax(offset, format.start - start + offset);
689 format.length = formatLength;
690 elidedFormats->append(format);
695 QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
698 return layout.engine()->elidedText(
699 Qt::TextElideMode(elideMode),
700 QFixed::fromReal(lineWidth),
703 line.textLength() + nextLine->textLength());
705 QString elideText = layout.text().mid(line.textStart(), line.textLength());
707 // QFontMetrics won't help eliding styled text.
708 elideText[elideText.length() - 1] = elideChar;
709 // Appending the elide character may push the line over the maximum width
710 // in which case the elided text will need to be elided.
711 QFontMetricsF metrics(layout.font());
712 if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
713 elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
720 Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
722 Returns the size of the final text. This can be used to position the text vertically (the text is
723 already absolutely positioned horizontally).
726 QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
730 bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
731 bool multilineElide = elideMode == QQuickText::ElideRight
733 && (q->heightValid() || maximumLineCountValid);
735 if ((!requireImplicitSize || (implicitWidthValid && implicitHeightValid))
736 && ((singlelineElide && q->width() <= 0.) || (multilineElide && q->heightValid() && q->height() <= 0.))) {
737 // we are elided and we have a zero width or height
738 widthExceeded = q->widthValid() && q->width() <= 0.;
739 heightExceeded = q->heightValid() && q->height() <= 0.;
743 emit q->truncatedChanged();
747 emit q->lineCountChanged();
750 QFontMetricsF fm(font);
751 qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : qCeil(fm.height()) * lineHeight();
752 *baseline = fm.ascent();
753 return QRectF(0, 0, 0, height);
756 bool shouldUseDesignMetrics = renderType != QQuickText::NativeRendering;
758 layout.setCacheEnabled(true);
759 QTextOption textOption = layout.textOption();
760 if (textOption.alignment() != q->effectiveHAlign()
761 || textOption.wrapMode() != QTextOption::WrapMode(wrapMode)
762 || textOption.useDesignMetrics() != shouldUseDesignMetrics) {
763 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
764 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
765 textOption.setUseDesignMetrics(shouldUseDesignMetrics);
766 layout.setTextOption(textOption);
768 if (layout.font() != font)
769 layout.setFont(font);
771 lineWidth = (q->widthValid() || implicitWidthValid) && q->width() > 0
774 qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
776 const bool customLayout = isLineLaidOutConnected();
777 const bool wasTruncated = truncated;
779 bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
781 bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
782 bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
783 && (q->heightValid() || (maximumLineCountValid && canWrap));
785 const bool pixelSize = font.pixelSize() != -1;
786 QString layoutText = layout.text();
788 int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
789 int smallFont = fontSizeMode() != QQuickText::FixedSize
790 ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
792 int scaledFontSize = largeFont;
794 widthExceeded = q->width() <= 0 && (singlelineElide || canWrap || horizontalFit);
795 heightExceeded = q->height() <= 0 && (multilineElide || verticalFit);
799 QFont scaledFont = font;
801 int visibleCount = 0;
809 int eos = multilengthEos;
811 // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
812 // doesn't fit within the item dimensions, or a binding to implicitWidth/Height changes
813 // the item dimensions.
817 scaledFont.setPixelSize(scaledFontSize);
819 scaledFont.setPointSize(scaledFontSize);
820 if (layout.font() != scaledFont)
821 layout.setFont(scaledFont);
824 layout.beginLayout();
826 bool wrapped = false;
827 bool truncateHeight = false;
830 int unwrappedLineCount = 1;
831 int maxLineCount = maximumLineCount();
833 qreal naturalHeight = 0;
834 qreal previousHeight = 0;
838 QTextLine line = layout.createLine();
839 for (visibleCount = 1; ; ++visibleCount) {
841 setupCustomLineGeometry(line, naturalHeight);
843 setLineGeometry(line, lineWidth, naturalHeight);
846 unelidedRect = br.united(line.naturalTextRect());
848 // Elide the previous line if the accumulated height of the text exceeds the height
850 if (multilineElide && naturalHeight > maxHeight && visibleCount > 1) {
852 heightExceeded = true;
853 if (eos != -1) // There's an abbreviated string available, skip the rest as it's
854 break; // all going to be discarded.
857 truncateHeight = true;
861 QTextLine previousLine = layout.lineAt(visibleCount - 1);
862 elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
863 ? elidedText(lineWidth, previousLine, &line)
864 : elidedText(lineWidth, previousLine);
865 elideStart = previousLine.textStart();
866 // elideEnd isn't required for right eliding.
868 height = previousHeight;
872 const QTextLine previousLine = line;
873 line = layout.createLine();
874 if (!line.isValid()) {
875 if (singlelineElide && visibleCount == 1 && previousLine.naturalTextWidth() > lineWidth) {
876 // Elide a single previousLine of text if its width exceeds the element width.
878 widthExceeded = true;
879 if (eos != -1) // There's an abbreviated string available.
883 elideText = layout.engine()->elidedText(
884 Qt::TextElideMode(elideMode),
885 QFixed::fromReal(lineWidth),
887 previousLine.textStart(),
888 previousLine.textLength());
889 elideStart = previousLine.textStart();
890 elideEnd = elideStart + previousLine.textLength();
893 height = naturalHeight;
897 const bool wrappedLine = layoutText.at(line.textStart() - 1) != QChar::LineSeparator;
898 wrapped |= wrappedLine;
901 ++unwrappedLineCount;
903 // Stop if the maximum number of lines has been reached and elide the last line
905 if (visibleCount == maxLineCount) {
907 heightExceeded |= wrapped;
909 if (multilineElide) {
911 if (eos != -1) // There's an abbreviated string available
913 elideText = wrappedLine
914 ? elidedText(lineWidth, previousLine, &line)
915 : elidedText(lineWidth, previousLine);
916 elideStart = previousLine.textStart();
917 // elideEnd isn't required for right eliding.
920 height = naturalHeight;
926 previousHeight = height;
927 height = naturalHeight;
929 widthExceeded |= wrapped;
931 // Save the implicit size of the text on the first layout only.
935 // If implicit sizes are required layout any additional lines up to the maximum line
937 if ((requireImplicitSize) && line.isValid() && unwrappedLineCount < maxLineCount) {
938 // Layout the remainder of the wrapped lines up to maxLineCount to get the implicit
940 for (int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
941 line = layout.createLine();
944 if (layoutText.at(line.textStart() - 1) == QChar::LineSeparator)
945 ++unwrappedLineCount;
946 setLineGeometry(line, lineWidth, naturalHeight);
949 // Create the remainder of the unwrapped lines up to maxLineCount to get the
951 if (line.isValid() && layoutText.at(line.textStart() + line.textLength()) != QChar::LineSeparator)
952 line = layout.createLine();
953 for (; line.isValid() && unwrappedLineCount <= maxLineCount; ++unwrappedLineCount)
954 line = layout.createLine();
958 const qreal naturalWidth = layout.maximumWidth();
960 bool wasInLayout = internalWidthUpdate;
961 internalWidthUpdate = true;
962 q->setImplicitSize(naturalWidth, naturalHeight);
963 internalWidthUpdate = wasInLayout;
965 // Update any variables that are dependent on the validity of the width or height.
966 singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
967 multilineElide = elideMode == QQuickText::ElideRight
969 && (q->heightValid() || maximumLineCountValid);
970 canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
972 horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
973 verticalFit = fontSizeMode() & QQuickText::VerticalFit
974 && (q->heightValid() || (maximumLineCountValid && canWrap));
976 const qreal oldWidth = lineWidth;
977 const qreal oldHeight = maxHeight;
979 lineWidth = q->widthValid() && q->width() > 0 ? q->width() : naturalWidth;
980 maxHeight = q->heightValid() ? q->height() : FLT_MAX;
982 // If the width of the item has changed and it's possible the result of wrapping,
983 // eliding, or scaling has changed do another layout.
984 if ((lineWidth < qMin(oldWidth, naturalWidth) || (widthExceeded && lineWidth > oldWidth))
985 && (singlelineElide || multilineElide || canWrap || horizontalFit)) {
986 widthExceeded = false;
987 heightExceeded = false;
991 // If the height of the item has changed and it's possible the result of eliding,
992 // line count truncation or scaling has changed, do another layout.
993 if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
994 && (multilineElide || (canWrap && maximumLineCountValid))) {
995 widthExceeded = false;
996 heightExceeded = false;
1000 // If the horizontal alignment is not left and the width was not valid we need to relayout
1001 // now that we know the maximum line width.
1002 if (!implicitWidthValid && lineCount > 1 && q->effectiveHAlign() != QQuickText::AlignLeft) {
1003 widthExceeded = false;
1004 heightExceeded = false;
1011 // If the next needs to be elided and there's an abbreviated string available
1012 // go back and do another layout with the abbreviated string.
1013 if (eos != -1 && elide) {
1014 int start = eos + 1;
1015 eos = text.indexOf(QLatin1Char('\x9c'), start);
1016 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
1017 layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
1018 layout.setText(layoutText);
1019 textHasChanged = true;
1025 if (!horizontalFit && !verticalFit)
1028 // Try and find a font size that better fits the dimensions of the element.
1029 if (horizontalFit) {
1030 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
1031 widthExceeded = true;
1032 largeFont = scaledFontSize - 1;
1033 if (smallFont > largeFont)
1035 scaledFontSize = (smallFont + largeFont) / 2;
1037 scaledFont.setPixelSize(scaledFontSize);
1039 scaledFont.setPointSize(scaledFontSize);
1041 } else if (!verticalFit) {
1042 smallFont = scaledFontSize;
1043 if (smallFont == largeFont)
1045 scaledFontSize = (smallFont + largeFont + 1) / 2;
1050 if (truncateHeight || unelidedRect.height() > maxHeight) {
1051 heightExceeded = true;
1052 largeFont = scaledFontSize - 1;
1053 if (smallFont > largeFont)
1055 scaledFontSize = (smallFont + largeFont) / 2;
1058 smallFont = scaledFontSize;
1059 if (smallFont == largeFont)
1061 scaledFontSize = (smallFont + largeFont + 1) / 2;
1066 implicitWidthValid = true;
1067 implicitHeightValid = true;
1069 if (eos != multilengthEos)
1074 elideLayout = new QTextLayout;
1075 elideLayout->setCacheEnabled(true);
1078 QList<QTextLayout::FormatRange> formats;
1079 switch (elideMode) {
1080 case QQuickText::ElideRight:
1081 elideFormats(elideStart, elideText.length() - 1, 0, &formats);
1083 case QQuickText::ElideLeft:
1084 elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
1086 case QQuickText::ElideMiddle: {
1087 const int index = elideText.indexOf(elideChar);
1089 elideFormats(elideStart, index, 0, &formats);
1091 elideEnd - elideText.length() + index + 1,
1092 elideText.length() - index - 1,
1101 elideLayout->setAdditionalFormats(formats);
1104 elideLayout->setFont(layout.font());
1105 elideLayout->setTextOption(layout.textOption());
1106 elideLayout->setText(elideText);
1107 elideLayout->beginLayout();
1109 QTextLine elidedLine = elideLayout->createLine();
1110 elidedLine.setPosition(QPointF(0, height));
1112 setupCustomLineGeometry(elidedLine, height, visibleCount - 1);
1114 setLineGeometry(elidedLine, lineWidth, height);
1116 elideLayout->endLayout();
1118 br = br.united(elidedLine.naturalTextRect());
1120 if (visibleCount == 1)
1121 layout.clearLayout();
1127 QTextLine firstLine = visibleCount == 1 && elideLayout
1128 ? elideLayout->lineAt(0)
1130 Q_ASSERT(firstLine.isValid());
1131 *baseline = firstLine.y() + firstLine.ascent();
1134 br.setHeight(height);
1136 //Update the number of visible lines
1137 if (lineCount != visibleCount) {
1138 lineCount = visibleCount;
1139 emit q->lineCountChanged();
1142 if (truncated != wasTruncated)
1143 emit q->truncatedChanged();
1148 void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
1151 line.setLineWidth(lineWidth);
1153 if (imgTags.isEmpty()) {
1154 line.setPosition(QPointF(line.position().x(), height));
1155 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
1160 qreal textHeight = line.height();
1161 qreal totalLineHeight = textHeight;
1163 QList<QQuickStyledTextImgTag *> imagesInLine;
1165 foreach (QQuickStyledTextImgTag *image, imgTags) {
1166 if (image->position >= line.textStart() &&
1167 image->position < line.textStart() + line.textLength()) {
1170 QUrl url = q->baseUrl().resolved(image->url);
1171 image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
1172 if (image->pix->isLoading()) {
1173 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
1174 if (!extra.isAllocated() || !extra->nbActiveDownloads)
1175 extra.value().nbActiveDownloads = 0;
1176 extra->nbActiveDownloads++;
1177 } else if (image->pix->isReady()) {
1178 if (!image->size.isValid()) {
1179 image->size = image->pix->implicitSize();
1180 // if the size of the image was not explicitly set, we need to
1181 // call updateLayout() once again.
1182 needToUpdateLayout = true;
1184 } else if (image->pix->isError()) {
1185 qmlInfo(q) << image->pix->error();
1189 qreal ih = qreal(image->size.height());
1190 if (image->align == QQuickStyledTextImgTag::Top)
1192 else if (image->align == QQuickStyledTextImgTag::Middle)
1193 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
1195 image->pos.setY(textHeight - ih);
1196 imagesInLine << image;
1197 textTop = qMax(textTop, qAbs(image->pos.y()));
1201 foreach (QQuickStyledTextImgTag *image, imagesInLine) {
1202 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
1203 image->pos.setX(line.cursorToX(image->position));
1204 image->pos.setY(image->pos.y() + height + textTop);
1205 visibleImgTags << image;
1208 line.setPosition(QPointF(line.position().x(), height + textTop));
1209 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
1213 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1215 void QQuickTextPrivate::ensureDoc()
1217 if (!extra.isAllocated() || !extra->doc) {
1219 extra.value().doc = new QQuickTextDocumentWithImageResources(q);
1220 extra->doc->setPageSize(QSizeF(0, 0));
1221 extra->doc->setDocumentMargin(0);
1222 extra->doc->setBaseUrl(q->baseUrl());
1223 qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
1224 q, QQuickText, SLOT(q_imagesLoaded()));
1230 \instantiates QQuickText
1231 \inqmlmodule QtQuick 2
1232 \ingroup qtquick-visual
1234 \brief Specifies how to add formatted text to a scene
1236 Text items can display both plain and rich text. For example, red text with
1237 a specific font and size can be defined like this:
1241 text: "Hello World!"
1242 font.family: "Helvetica"
1248 Rich text is defined using HTML-style markup:
1252 text: "<b>Hello</b> <i>World!</i>"
1256 \image declarative-text.png
1258 If height and width are not explicitly set, Text will attempt to determine how
1259 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1260 prefer width to height (all text will be placed on a single line).
1262 The \l elide property can alternatively be used to fit a single line of
1263 plain text to a set width.
1265 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1266 HTML img tags that load remote images, the text is reloaded.
1268 Text provides read-only text. For editable text, see \l TextEdit.
1270 \sa {declarative/text/fonts}{Fonts example}
1272 QQuickText::QQuickText(QQuickItem *parent)
1273 : QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1279 QQuickText::~QQuickText()
1284 \qmlproperty bool QtQuick2::Text::clip
1285 This property holds whether the text is clipped.
1287 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1289 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1293 \qmlsignal QtQuick2::Text::onLineLaidOut(object line)
1295 This handler is called for each line of text that is laid out during the layout
1296 process. The specified \a line object provides more details about the line that
1297 is currently being laid out.
1299 This gives the opportunity to position and resize a line as it is being laid out.
1300 It can for example be used to create columns or lay out text around objects.
1302 The properties of the specified \a line object are:
1304 \li number (read-only)
1311 For example, this will move the first 5 lines of a Text item by 100 pixels to the right:
1314 if (line.number < 5) {
1315 line.x = line.x + 100
1316 line.width = line.width - 100
1323 \qmlsignal QtQuick2::Text::onLinkActivated(string link)
1325 This handler is called when the user clicks on a link embedded in the text.
1326 The link must be in rich text or HTML format and the
1327 \a link string provides access to the particular link.
1329 \snippet qml/text/onLinkActivated.qml 0
1331 The example code will display the text
1332 "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
1334 Clicking on the highlighted link will output
1335 \tt{http://qt.nokia.com link activated} to the console.
1339 \qmlproperty string QtQuick2::Text::font.family
1341 Sets the family name of the font.
1343 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1344 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1345 If the family isn't available a family will be set using the font matching algorithm.
1349 \qmlproperty bool QtQuick2::Text::font.bold
1351 Sets whether the font weight is bold.
1355 \qmlproperty enumeration QtQuick2::Text::font.weight
1357 Sets the font's weight.
1359 The weight can be one of:
1362 \li Font.Normal - the default
1369 Text { text: "Hello"; font.weight: Font.DemiBold }
1374 \qmlproperty bool QtQuick2::Text::font.italic
1376 Sets whether the font has an italic style.
1380 \qmlproperty bool QtQuick2::Text::font.underline
1382 Sets whether the text is underlined.
1386 \qmlproperty bool QtQuick2::Text::font.strikeout
1388 Sets whether the font has a strikeout style.
1392 \qmlproperty real QtQuick2::Text::font.pointSize
1394 Sets the font size in points. The point size must be greater than zero.
1398 \qmlproperty int QtQuick2::Text::font.pixelSize
1400 Sets the font size in pixels.
1402 Using this function makes the font device dependent.
1403 Use \c pointSize to set the size of the font in a device independent manner.
1407 \qmlproperty real QtQuick2::Text::font.letterSpacing
1409 Sets the letter spacing for the font.
1411 Letter spacing changes the default spacing between individual letters in the font.
1412 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1416 \qmlproperty real QtQuick2::Text::font.wordSpacing
1418 Sets the word spacing for the font.
1420 Word spacing changes the default spacing between individual words.
1421 A positive value increases the word spacing by a corresponding amount of pixels,
1422 while a negative value decreases the inter-word spacing accordingly.
1426 \qmlproperty enumeration QtQuick2::Text::font.capitalization
1428 Sets the capitalization for the text.
1431 \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
1432 \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
1433 \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
1434 \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
1435 \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
1439 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1442 QFont QQuickText::font() const
1444 Q_D(const QQuickText);
1445 return d->sourceFont;
1448 void QQuickText::setFont(const QFont &font)
1451 if (d->sourceFont == font)
1454 d->sourceFont = font;
1455 QFont oldFont = d->font;
1458 if (d->font.pointSizeF() != -1) {
1460 qreal size = qRound(d->font.pointSizeF()*2.0);
1461 d->font.setPointSizeF(size/2.0);
1464 if (oldFont != d->font) {
1465 // if the format changes the size of the text
1466 // with headings or <font> tag, we need to re-parse
1467 if (d->formatModifiesFontSize)
1468 d->textHasChanged = true;
1469 d->implicitWidthValid = false;
1470 d->implicitHeightValid = false;
1474 emit fontChanged(d->sourceFont);
1478 \qmlproperty string QtQuick2::Text::text
1480 The text to display. Text supports both plain and rich text strings.
1482 The item will try to automatically determine whether the text should
1483 be treated as styled text. This determination is made using Qt::mightBeRichText().
1485 QString QQuickText::text() const
1487 Q_D(const QQuickText);
1491 void QQuickText::setText(const QString &n)
1497 d->richText = d->format == RichText;
1498 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1500 if (isComponentComplete()) {
1503 d->extra->doc->setText(n);
1504 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1506 d->rightToLeftText = d->text.isRightToLeft();
1508 d->determineHorizontalAlignment();
1510 d->textHasChanged = true;
1511 d->implicitWidthValid = false;
1512 d->implicitHeightValid = false;
1513 qDeleteAll(d->imgTags);
1516 emit textChanged(d->text);
1520 \qmlproperty color QtQuick2::Text::color
1524 An example of green text defined using hexadecimal notation:
1532 An example of steel blue text defined using an SVG color name:
1540 QColor QQuickText::color() const
1542 Q_D(const QQuickText);
1543 return QColor::fromRgba(d->color);
1546 void QQuickText::setColor(const QColor &color)
1549 QRgb rgb = color.rgba();
1550 if (d->color == rgb)
1554 if (isComponentComplete()) {
1555 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1558 emit colorChanged();
1562 \qmlproperty color QtQuick2::Text::linkColor
1564 The color of links in the text.
1566 This property works with the StyledText \l textFormat, but not with RichText.
1567 Link color in RichText can be specified by including CSS style tags in the
1571 QColor QQuickText::linkColor() const
1573 Q_D(const QQuickText);
1574 return QColor::fromRgba(d->linkColor);
1577 void QQuickText::setLinkColor(const QColor &color)
1580 QRgb rgb = color.rgba();
1581 if (d->linkColor == rgb)
1586 emit linkColorChanged();
1590 \qmlproperty enumeration QtQuick2::Text::style
1592 Set an additional text style.
1594 Supported text styles are:
1596 \li Text.Normal - the default
1604 Text { font.pointSize: 24; text: "Normal" }
1605 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1606 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1607 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1611 \image declarative-textstyle.png
1613 QQuickText::TextStyle QQuickText::style() const
1615 Q_D(const QQuickText);
1619 void QQuickText::setStyle(QQuickText::TextStyle style)
1622 if (d->style == style)
1626 if (isComponentComplete()) {
1627 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1630 emit styleChanged(d->style);
1634 \qmlproperty color QtQuick2::Text::styleColor
1636 Defines the secondary color used by text styles.
1638 \c styleColor is used as the outline color for outlined text, and as the
1639 shadow color for raised or sunken text. If no style has been set, it is not
1643 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1648 QColor QQuickText::styleColor() const
1650 Q_D(const QQuickText);
1651 return QColor::fromRgba(d->styleColor);
1654 void QQuickText::setStyleColor(const QColor &color)
1657 QRgb rgb = color.rgba();
1658 if (d->styleColor == rgb)
1661 d->styleColor = rgb;
1662 if (isComponentComplete()) {
1663 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1666 emit styleColorChanged();
1670 \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
1671 \qmlproperty enumeration QtQuick2::Text::verticalAlignment
1672 \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
1674 Sets the horizontal and vertical alignment of the text within the Text items
1675 width and height. By default, the text is vertically aligned to the top. Horizontal
1676 alignment follows the natural alignment of the text, for example text that is read
1677 from left to right will be aligned to the left.
1679 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1680 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1681 and \c Text.AlignVCenter.
1683 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1684 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1685 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1688 When using the attached property LayoutMirroring::enabled to mirror application
1689 layouts, the horizontal alignment of text will also be mirrored. However, the property
1690 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1691 of Text, use the read-only property \c effectiveHorizontalAlignment.
1693 QQuickText::HAlignment QQuickText::hAlign() const
1695 Q_D(const QQuickText);
1699 void QQuickText::setHAlign(HAlignment align)
1702 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1703 d->hAlignImplicit = false;
1704 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1708 void QQuickText::resetHAlign()
1711 d->hAlignImplicit = true;
1712 if (isComponentComplete() && d->determineHorizontalAlignment())
1716 QQuickText::HAlignment QQuickText::effectiveHAlign() const
1718 Q_D(const QQuickText);
1719 QQuickText::HAlignment effectiveAlignment = d->hAlign;
1720 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
1721 switch (d->hAlign) {
1722 case QQuickText::AlignLeft:
1723 effectiveAlignment = QQuickText::AlignRight;
1725 case QQuickText::AlignRight:
1726 effectiveAlignment = QQuickText::AlignLeft;
1732 return effectiveAlignment;
1735 bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
1738 if (hAlign != alignment || forceAlign) {
1739 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
1742 emit q->horizontalAlignmentChanged(hAlign);
1743 if (oldEffectiveHAlign != q->effectiveHAlign())
1744 emit q->effectiveHorizontalAlignmentChanged();
1750 bool QQuickTextPrivate::determineHorizontalAlignment()
1752 if (hAlignImplicit) {
1753 bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
1754 return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
1759 void QQuickTextPrivate::mirrorChange()
1762 if (q->isComponentComplete()) {
1763 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
1765 emit q->effectiveHorizontalAlignmentChanged();
1770 QQuickText::VAlignment QQuickText::vAlign() const
1772 Q_D(const QQuickText);
1776 void QQuickText::setVAlign(VAlignment align)
1779 if (d->vAlign == align)
1783 emit verticalAlignmentChanged(align);
1787 \qmlproperty enumeration QtQuick2::Text::wrapMode
1789 Set this property to wrap the text to the Text item's width. The text will only
1790 wrap if an explicit width has been set. wrapMode can be one of:
1793 \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
1794 \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
1795 \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
1796 \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.
1799 QQuickText::WrapMode QQuickText::wrapMode() const
1801 Q_D(const QQuickText);
1805 void QQuickText::setWrapMode(WrapMode mode)
1808 if (mode == d->wrapMode)
1814 emit wrapModeChanged();
1818 \qmlproperty int QtQuick2::Text::lineCount
1820 Returns the number of lines visible in the text item.
1822 This property is not supported for rich text.
1824 \sa maximumLineCount
1826 int QQuickText::lineCount() const
1828 Q_D(const QQuickText);
1829 return d->lineCount;
1833 \qmlproperty bool QtQuick2::Text::truncated
1835 Returns true if the text has been truncated due to \l maximumLineCount
1838 This property is not supported for rich text.
1840 \sa maximumLineCount, elide
1842 bool QQuickText::truncated() const
1844 Q_D(const QQuickText);
1845 return d->truncated;
1849 \qmlproperty int QtQuick2::Text::maximumLineCount
1851 Set this property to limit the number of lines that the text item will show.
1852 If elide is set to Text.ElideRight, the text will be elided appropriately.
1853 By default, this is the value of the largest possible integer.
1855 This property is not supported for rich text.
1857 \sa lineCount, elide
1859 int QQuickText::maximumLineCount() const
1861 Q_D(const QQuickText);
1862 return d->maximumLineCount();
1865 void QQuickText::setMaximumLineCount(int lines)
1869 d->maximumLineCountValid = lines==INT_MAX ? false : true;
1870 if (d->maximumLineCount() != lines) {
1871 d->extra.value().maximumLineCount = lines;
1872 d->implicitHeightValid = false;
1874 emit maximumLineCountChanged();
1878 void QQuickText::resetMaximumLineCount()
1881 setMaximumLineCount(INT_MAX);
1882 if (d->truncated != false) {
1883 d->truncated = false;
1884 emit truncatedChanged();
1889 \qmlproperty enumeration QtQuick2::Text::textFormat
1891 The way the text property should be displayed.
1893 Supported text formats are:
1896 \li Text.AutoText (default)
1902 If the text format is \c Text.AutoText the Text item
1903 will automatically determine whether the text should be treated as
1904 styled text. This determination is made using Qt::mightBeRichText()
1905 which uses a fast and therefore simple heuristic. It mainly checks
1906 whether there is something that looks like a tag before the first
1907 line break. Although the result may be correct for common cases,
1908 there is no guarantee.
1910 Text.StyledText is an optimized format supporting some basic text
1911 styling markup, in the style of html 3.2:
1915 <strong></strong> - bold
1919 <u> - underlined text
1920 <font color="color_name" size="1-7"></font>
1921 <h1> to <h6> - headers
1922 <a href=""> - anchor
1923 <img src="" align="top,middle,bottom" width="" height=""> - inline images
1924 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
1925 <pre></pre> - preformatted
1929 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
1938 text: "<b>Hello</b> <i>World!</i>"
1942 textFormat: Text.RichText
1943 text: "<b>Hello</b> <i>World!</i>"
1947 textFormat: Text.PlainText
1948 text: "<b>Hello</b> <i>World!</i>"
1952 \li \image declarative-textformat.png
1955 QQuickText::TextFormat QQuickText::textFormat() const
1957 Q_D(const QQuickText);
1961 void QQuickText::setTextFormat(TextFormat format)
1964 if (format == d->format)
1967 bool wasRich = d->richText;
1968 d->richText = format == RichText;
1969 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
1971 if (isComponentComplete()) {
1972 if (!wasRich && d->richText) {
1974 d->extra->doc->setText(d->text);
1975 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
1977 d->rightToLeftText = d->text.isRightToLeft();
1979 d->determineHorizontalAlignment();
1983 emit textFormatChanged(d->format);
1987 \qmlproperty enumeration QtQuick2::Text::elide
1989 Set this property to elide parts of the text fit to the Text item's width.
1990 The text will only elide if an explicit width has been set.
1992 This property cannot be used with rich text.
1996 \li Text.ElideNone - the default
1998 \li Text.ElideMiddle
2002 If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
2003 text. The text will only elide if \c maximumLineCount, or \c height has been set.
2004 If both \c maximumLineCount and \c height are set, \c maximumLineCount will
2005 apply unless the lines do not fit in the height allowed.
2007 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
2008 the first string that fits will be used, otherwise the last will be elided.
2010 Multi-length strings are ordered from longest to shortest, separated by the
2011 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
2013 QQuickText::TextElideMode QQuickText::elideMode() const
2015 Q_D(const QQuickText);
2016 return d->elideMode;
2019 void QQuickText::setElideMode(QQuickText::TextElideMode mode)
2022 if (mode == d->elideMode)
2025 d->elideMode = mode;
2028 emit elideModeChanged(mode);
2032 \qmlproperty url QtQuick2::Text::baseUrl
2034 This property specifies a base URL which is used to resolve relative URLs
2037 Urls are resolved to be within the same directory as the target of the base
2038 URL meaning any portion of the path after the last '/' will be ignored.
2041 \header \li Base URL \li Relative URL \li Resolved URL
2042 \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
2043 \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
2044 \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
2045 \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
2046 \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
2047 \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
2048 \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
2051 The default value is the url of the QML file instantiating the Text item.
2054 QUrl QQuickText::baseUrl() const
2056 Q_D(const QQuickText);
2057 if (d->baseUrl.isEmpty()) {
2058 if (QQmlContext *context = qmlContext(this))
2059 const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
2064 void QQuickText::setBaseUrl(const QUrl &url)
2067 if (baseUrl() != url) {
2072 d->extra->doc->setBaseUrl(url);
2074 if (d->styledText) {
2075 d->textHasChanged = true;
2076 qDeleteAll(d->imgTags);
2080 emit baseUrlChanged();
2084 void QQuickText::resetBaseUrl()
2086 if (QQmlContext *context = qmlContext(this))
2087 setBaseUrl(context->baseUrl());
2093 QRectF QQuickText::boundingRect() const
2095 Q_D(const QQuickText);
2097 QRectF rect = d->layedOutTextRect;
2098 rect.moveLeft(QQuickTextUtil::alignedX(rect.width(), width(), d->hAlign));
2099 rect.moveTop(QQuickTextUtil::alignedY(rect.height(), height(), d->vAlign));
2101 if (d->style != Normal)
2102 rect.adjust(-1, 0, 1, 2);
2103 // Could include font max left/right bearings to either side of rectangle.
2108 QRectF QQuickText::clipRect() const
2110 Q_D(const QQuickText);
2112 QRectF rect = QQuickImplicitSizeItem::clipRect();
2113 if (d->style != Normal)
2114 rect.adjust(-1, 0, 1, 2);
2119 void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2122 if (d->text.isEmpty()) {
2123 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2127 bool widthChanged = newGeometry.width() != oldGeometry.width();
2128 bool heightChanged = newGeometry.height() != oldGeometry.height();
2129 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2130 bool elide = d->elideMode != QQuickText::ElideNone;
2131 bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
2132 bool verticalScale = (d->fontSizeMode() & QQuickText::VerticalFit) && heightValid();
2134 bool widthMaximum = newGeometry.width() >= oldGeometry.width() && !d->widthExceeded;
2135 bool heightMaximum = newGeometry.height() >= oldGeometry.height() && !d->heightExceeded;
2137 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2138 goto geomChangeDone;
2140 if (effectiveHAlign() != QQuickText::AlignLeft && widthChanged) {
2141 // If the width has changed and we're not left aligned do an update so the text is
2142 // repositioned even if a full layout isn't required.
2143 d->updateType = QQuickTextPrivate::UpdatePaintNode;
2147 if (!wrapped && !elide && !scaleFont)
2148 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2150 if (elide // eliding and dimensions were and remain invalid;
2151 && ((widthValid() && oldGeometry.width() <= 0 && newGeometry.width() <= 0)
2152 || (heightValid() && oldGeometry.height() <= 0 && newGeometry.height() <= 0))) {
2153 goto geomChangeDone;
2156 if (widthMaximum && heightMaximum && !d->isLineLaidOutConnected()) // Size is sufficient and growing.
2157 goto geomChangeDone;
2159 if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
2160 if (newGeometry.height() > oldGeometry.height()) {
2161 if (!d->heightExceeded) // Height is adequate and growing.
2162 goto geomChangeDone;
2163 if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
2164 goto geomChangeDone;
2165 } else if (newGeometry.height() < oldGeometry.height()) {
2166 if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
2167 goto geomChangeDone;
2169 if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
2170 && d->elideMode != QQuickText::ElideRight
2171 && !(d->maximumLineCountValid && d->widthExceeded)) {
2172 goto geomChangeDone;
2175 } else if (!heightChanged && widthMaximum) {
2176 goto geomChangeDone;
2179 if (d->updateOnComponentComplete || d->textHasChanged) {
2180 // We need to re-elide
2183 // We just need to re-layout
2188 QQuickItem::geometryChanged(newGeometry, oldGeometry);
2191 void QQuickText::triggerPreprocess()
2194 if (d->updateType == QQuickTextPrivate::UpdateNone)
2195 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2199 QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2204 if (d->text.isEmpty()) {
2209 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != 0) {
2210 // Update done in preprocess() in the nodes
2211 d->updateType = QQuickTextPrivate::UpdateNone;
2215 d->updateType = QQuickTextPrivate::UpdateNone;
2217 const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height(), height(), d->vAlign);
2219 QQuickTextNode *node = 0;
2221 node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
2223 node = static_cast<QQuickTextNode *>(oldNode);
2226 node->setUseNativeRenderer(d->renderType == NativeRendering);
2227 node->deleteContent();
2228 node->setMatrix(QMatrix4x4());
2230 const QColor color = QColor::fromRgba(d->color);
2231 const QColor styleColor = QColor::fromRgba(d->styleColor);
2232 const QColor linkColor = QColor::fromRgba(d->linkColor);
2235 const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), width(), d->hAlign);
2237 node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor);
2238 } else if (d->layedOutTextRect.width() > 0) {
2239 const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, width(), d->hAlign);
2240 int unelidedLineCount = d->lineCount;
2242 unelidedLineCount -= 1;
2243 if (unelidedLineCount > 0) {
2244 node->addTextLayout(
2247 color, d->style, styleColor, linkColor,
2248 QColor(), QColor(), -1, -1,
2249 0, unelidedLineCount);
2252 node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
2254 foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
2255 QQuickPixmap *pix = img->pix;
2256 if (pix && pix->isReady())
2257 node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
2263 void QQuickText::updatePolish()
2270 \qmlproperty real QtQuick2::Text::contentWidth
2272 Returns the width of the text, including width past the width
2273 which is covered due to insufficient wrapping if WrapMode is set.
2275 qreal QQuickText::contentWidth() const
2277 Q_D(const QQuickText);
2278 return d->layedOutTextRect.width();
2282 \qmlproperty real QtQuick2::Text::contentHeight
2284 Returns the height of the text, including height past the height
2285 which is covered due to there being more text than fits in the set height.
2287 qreal QQuickText::contentHeight() const
2289 Q_D(const QQuickText);
2290 return d->layedOutTextRect.height();
2294 \qmlproperty real QtQuick2::Text::lineHeight
2296 Sets the line height for the text.
2297 The value can be in pixels or a multiplier depending on lineHeightMode.
2299 The default value is a multiplier of 1.0.
2300 The line height must be a positive value.
2302 qreal QQuickText::lineHeight() const
2304 Q_D(const QQuickText);
2305 return d->lineHeight();
2308 void QQuickText::setLineHeight(qreal lineHeight)
2312 if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
2315 d->extra.value().lineHeight = lineHeight;
2316 d->implicitHeightValid = false;
2318 emit lineHeightChanged(lineHeight);
2322 \qmlproperty enumeration QtQuick2::Text::lineHeightMode
2324 This property determines how the line height is specified.
2325 The possible values are:
2328 \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
2329 line (as a multiplier). For example, set to 2 for double spacing.
2330 \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
2333 QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2335 Q_D(const QQuickText);
2336 return d->lineHeightMode();
2339 void QQuickText::setLineHeightMode(LineHeightMode mode)
2342 if (mode == d->lineHeightMode())
2345 d->implicitHeightValid = false;
2346 d->extra.value().lineHeightMode = mode;
2349 emit lineHeightModeChanged(mode);
2353 \qmlproperty enumeration QtQuick2::Text::fontSizeMode
2355 This property specifies how the font size of the displayed text is determined.
2356 The possible values are:
2359 \li Text.FixedSize (default) - The size specified by \l font.pixelSize
2360 or \l font.pointSize is used.
2361 \li Text.HorizontalFit - The largest size up to the size specified that fits
2362 within the width of the item without wrapping is used.
2363 \li Text.VerticalFit - The largest size up to the size specified that fits
2364 the height of the item is used.
2365 \li Text.Fit - The largest size up to the size specified the fits within the
2366 width and height of the item is used.
2369 The font size of fitted text has a minimum bound specified by the
2370 minimumPointSize or minimumPixelSize property and maximum bound specified
2371 by either the \l font.pointSize or \l font.pixelSize properties.
2373 If the text does not fit within the item bounds with the minimum font size
2374 the text will be elided as per the \l elide property.
2377 QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2379 Q_D(const QQuickText);
2380 return d->fontSizeMode();
2383 void QQuickText::setFontSizeMode(FontSizeMode mode)
2386 if (d->fontSizeMode() == mode)
2391 d->extra.value().fontSizeMode = mode;
2392 emit fontSizeModeChanged();
2396 \qmlproperty int QtQuick2::Text::minimumPixelSize
2398 This property specifies the minimum font pixel size of text scaled by the
2399 fontSizeMode property.
2401 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2402 property is ignored.
2405 int QQuickText::minimumPixelSize() const
2407 Q_D(const QQuickText);
2408 return d->minimumPixelSize();
2411 void QQuickText::setMinimumPixelSize(int size)
2414 if (d->minimumPixelSize() == size)
2417 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2419 d->extra.value().minimumPixelSize = size;
2420 emit minimumPixelSizeChanged();
2424 \qmlproperty int QtQuick2::Text::minimumPointSize
2426 This property specifies the minimum font point \l size of text scaled by
2427 the fontSizeMode property.
2429 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2430 property is ignored.
2433 int QQuickText::minimumPointSize() const
2435 Q_D(const QQuickText);
2436 return d->minimumPointSize();
2439 void QQuickText::setMinimumPointSize(int size)
2442 if (d->minimumPointSize() == size)
2445 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
2447 d->extra.value().minimumPointSize = size;
2448 emit minimumPointSizeChanged();
2452 Returns the number of resources (images) that are being loaded asynchronously.
2454 int QQuickText::resourcesLoading() const
2456 Q_D(const QQuickText);
2457 if (d->richText && d->extra.isAllocated() && d->extra->doc)
2458 return d->extra->doc->resourcesLoading();
2463 void QQuickText::componentComplete()
2466 if (d->updateOnComponentComplete) {
2469 d->extra->doc->setText(d->text);
2470 d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
2472 d->rightToLeftText = d->text.isRightToLeft();
2474 d->determineHorizontalAlignment();
2476 QQuickItem::componentComplete();
2477 if (d->updateOnComponentComplete)
2481 QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mousePos)
2483 for (int i = 0; i < layout->lineCount(); ++i) {
2484 QTextLine line = layout->lineAt(i);
2485 if (line.naturalTextRect().contains(mousePos)) {
2486 int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter);
2487 foreach (const QTextLayout::FormatRange &formatRange, layout->additionalFormats()) {
2488 if (formatRange.format.isAnchor()
2489 && charPos >= formatRange.start
2490 && charPos < formatRange.start + formatRange.length) {
2491 return formatRange.format.anchorHref();
2500 QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const
2502 Q_Q(const QQuickText);
2503 QPointF translatedMousePos = mousePos;
2504 translatedMousePos.ry() -= QQuickTextUtil::alignedY(layedOutTextRect.height(), q->height(), vAlign);
2506 QString link = anchorAt(&layout, translatedMousePos);
2507 if (link.isEmpty() && elideLayout)
2508 link = anchorAt(elideLayout, translatedMousePos);
2510 } else if (richText && extra.isAllocated() && extra->doc) {
2511 translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), q->width(), hAlign);
2512 return extra->doc->documentLayout()->anchorAt(translatedMousePos);
2517 bool QQuickTextPrivate::isLinkActivatedConnected()
2520 IS_SIGNAL_CONNECTED(q, QQuickText, linkActivated, (const QString &));
2524 void QQuickText::mousePressEvent(QMouseEvent *event)
2529 if (d->isLinkActivatedConnected())
2530 link = d->anchorAt(event->localPos());
2532 if (link.isEmpty()) {
2533 event->setAccepted(false);
2535 d->extra.value().activeLink = link;
2538 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2540 if (!event->isAccepted())
2541 QQuickItem::mousePressEvent(event);
2546 void QQuickText::mouseReleaseEvent(QMouseEvent *event)
2550 // ### confirm the link, and send a signal out
2553 if (d->isLinkActivatedConnected())
2554 link = d->anchorAt(event->localPos());
2556 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2557 emit linkActivated(d->extra->activeLink);
2559 event->setAccepted(false);
2561 if (!event->isAccepted())
2562 QQuickItem::mouseReleaseEvent(event);