** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
-** This file is part of the QtDeclarative module of the Qt Toolkit.
+** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
#include "qquicktext_p_p.h"
#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qqmlglobal_p.h>
#include <private/qsgadaptationlayer_p.h>
#include "qquicktextnode_p.h"
#include "qquickimage_p_p.h"
#include <QtQuick/private/qsgtexture_p.h>
-#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtQml/qqmlinfo.h>
#include <QtGui/qevent.h>
#include <QtGui/qabstracttextdocumentlayout.h>
#include <QtGui/qpainter.h>
#include <QtGui/qtextobject.h>
#include <QtGui/qtextcursor.h>
#include <QtGui/qguiapplication.h>
-#include <QtGui/qinputpanel.h>
+#include <QtGui/qinputmethod.h>
#include <private/qtextengine_p.h>
-#include <private/qdeclarativestyledtext_p.h>
-#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+#include <private/qquickstyledtext_p.h>
+#include <QtQuick/private/qquickpixmapcache_p.h>
#include <qmath.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
-extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
-
-DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
QQuickTextPrivate::QQuickTextPrivate()
-: color((QRgb)0), style(QQuickText::Normal), hAlign(QQuickText::AlignLeft),
- vAlign(QQuickText::AlignTop), elideMode(QQuickText::ElideNone),
- format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap), lineHeight(1),
- lineHeightMode(QQuickText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
- maximumLineCountValid(false), fontSizeMode(QQuickText::FixedSize), minimumPixelSize(12),
- minimumPointSize(12), imageCache(0), texture(0),
- imageCacheDirty(false), updateOnComponentComplete(true),
- richText(false), styledText(false), singleline(false), cacheAllTextAsImage(true),
- disableDistanceField(false), internalWidthUpdate(false),
- requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
- layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), textHasChanged(true),
- needToUpdateLayout(false), naturalWidth(0), doc(0), elideLayout(0), textLine(0), nodeType(NodeIsNull),
- updateType(UpdatePaintNode), nbActiveDownloads(0)
-
+ : elideLayout(0), textLine(0)
#if defined(Q_OS_MAC)
-, layoutThread(0), paintingThread(0)
+ , layoutThread(0), paintingThread(0)
#endif
+ , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
+ , lineCount(1), multilengthEos(-1)
+ , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
+ , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
+ , style(QQuickText::Normal)
+ , updateType(UpdatePaintNode)
+ , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
+ , styledText(false), singleline(false), internalWidthUpdate(false), requireImplicitWidth(false)
+ , truncated(false), hAlignImplicit(true), rightToLeftText(false)
+ , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
+{
+}
+
+QQuickTextPrivate::ExtraData::ExtraData()
+ : lineHeight(1.0)
+ , doc(0)
+ , minimumPixelSize(12)
+ , minimumPointSize(12)
+ , nbActiveDownloads(0)
+ , maximumLineCount(INT_MAX)
+ , lineHeightMode(QQuickText::ProportionalHeight)
+ , fontSizeMode(QQuickText::FixedSize)
{
- cacheAllTextAsImage = enableImageCache();
- disableDistanceField = qmlDisableDistanceField();
}
void QQuickTextPrivate::init()
QVariant QQuickTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
{
- QDeclarativeContext *context = qmlContext(parent());
+ QQmlContext *context = qmlContext(parent());
QUrl url = m_baseUrl.resolved(name);
if (type == QTextDocument::ImageResource) {
- QDeclarativePixmap *p = loadPixmap(context, url);
+ QQuickPixmap *p = loadPixmap(context, url);
return p->image();
}
QSizeF size(width, height);
if (!hasWidth || !hasHeight) {
- QDeclarativeContext *context = qmlContext(parent());
+ QQmlContext *context = qmlContext(parent());
QUrl url = m_baseUrl.resolved(QUrl(imageFormat.name()));
- QDeclarativePixmap *p = loadPixmap(context, url);
+ QQuickPixmap *p = loadPixmap(context, url);
if (!p->isReady()) {
if (!hasWidth)
size.setWidth(16);
QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
{
- QDeclarativeContext *context = qmlContext(parent());
+ QQmlContext *context = qmlContext(parent());
QUrl url = m_baseUrl.resolved(QUrl(format.name()));
- QDeclarativePixmap *p = loadPixmap(context, url);
+ QQuickPixmap *p = loadPixmap(context, url);
return p->image();
}
}
}
-QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
- QDeclarativeContext *context, const QUrl &url)
+QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
+ QQmlContext *context, const QUrl &url)
{
- QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
+ QHash<QUrl, QQuickPixmap *>::Iterator iter = m_resources.find(url);
if (iter == m_resources.end()) {
- QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
+ QQuickPixmap *p = new QQuickPixmap(context->engine(), url);
iter = m_resources.insert(url, p);
if (p->isLoading()) {
}
}
- QDeclarativePixmap *p = *iter;
+ QQuickPixmap *p = *iter;
if (p->isError()) {
if (!errors.contains(url)) {
errors.insert(url);
void QQuickTextDocumentWithImageResources::clearResources()
{
- foreach (QDeclarativePixmap *pixmap, m_resources)
+ foreach (QQuickPixmap *pixmap, m_resources)
pixmap->clear(this);
qDeleteAll(m_resources);
m_resources.clear();
{
delete elideLayout;
delete textLine; textLine = 0;
- delete imageCache;
qDeleteAll(imgTags);
imgTags.clear();
}
if (!richText) {
if (textHasChanged) {
if (styledText && !text.isEmpty()) {
- QDeclarativeStyledText::parse(text, layout, imgTags, qmlContext(q), !maximumLineCountValid);
+ layout.setFont(font);
+ // needs temporary bool because formatModifiesFontSize is in a bit-field
+ bool fontSizeModified = false;
+ QQuickStyledText::parse(text, layout, imgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
+ formatModifiesFontSize = fontSizeModified;
} else {
layout.clearAdditionalFormats();
QString tmp = text;
- tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
+ if (multilengthEos != -1) {
+ tmp = tmp.mid(0, multilengthEos);
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ } else if (tmp.contains(QLatin1Char('\n'))) {
+ // Replace always does a detach. Checking for the new line character first
+ // means iterating over those items again if found but prevents a realloc
+ // otherwise.
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ }
layout.setText(tmp);
}
textHasChanged = false;
} else {
ensureDoc();
QTextBlockFormat::LineHeightTypes type;
- type = lineHeightMode == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
+ type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
QTextBlockFormat blockFormat;
- blockFormat.setLineHeight((lineHeightMode == QQuickText::FixedHeight ? lineHeight : lineHeight * 100), type);
- for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
+ blockFormat.setLineHeight((lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), type);
+ for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
QTextCursor cursor(it);
cursor.mergeBlockFormat(blockFormat);
}
{
Q_D(QQuickText);
- (d->nbActiveDownloads)--;
+ (d->extra->nbActiveDownloads)--;
// when all the remote images have been downloaded,
// if one of the sizes was not specified at parsing time
// we use the implicit size from pixmapcache and re-layout.
- if (d->nbActiveDownloads == 0) {
+ if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
bool needToUpdateLayout = false;
- foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
+ foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
if (!img->size.isValid()) {
img->size = img->pix->implicitSize();
needToUpdateLayout = true;
}
}
+void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy)
+{
+ Q_Q(QQuickText);
+
+ qreal yoff = 0;
+
+ if (q->heightValid()) {
+ if (vAlign == QQuickText::AlignBottom)
+ yoff = dy;
+ else if (vAlign == QQuickText::AlignVCenter)
+ yoff = dy/2;
+ }
+
+ q->setBaselineOffset(baseline + yoff);
+}
+
void QQuickTextPrivate::updateSize()
{
Q_Q(QQuickText);
return;
}
- invalidateImageCache();
-
- QFontMetrics fm(font);
- if (text.isEmpty()) {
- qreal fontHeight = fm.height();
+ if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
+ // How much more expensive is it to just do a full layout on an empty string here?
+ // There may be subtle differences in the height and baseline calculations between
+ // QTextLayout and QFontMetrics and the number of variables that can affect the size
+ // and position of a line is increasing.
+ QFontMetricsF fm(font);
+ qreal fontHeight = qCeil(fm.height()); // QScriptLine and therefore QTextLine rounds up
+ if (!richText) { // line height, so we will as well.
+ fontHeight = lineHeightMode() == QQuickText::FixedHeight
+ ? lineHeight()
+ : fontHeight * lineHeight();
+ }
+ updateBaseline(fm.ascent(), q->height() - fontHeight);
q->setImplicitSize(0, fontHeight);
- paintedSize = QSize(0, fontHeight);
- emit q->paintedSizeChanged();
+ layedOutTextRect = QRectF(0, 0, 0, fontHeight);
+ emit q->contentSizeChanged();
updateType = UpdatePaintNode;
q->update();
return;
}
- int dy = q->height();
- QSize size(0, 0);
+ qreal naturalWidth = 0;
+ QSizeF size(0, 0);
+ QSizeF previousSize = layedOutTextRect.size();
#if defined(Q_OS_MAC)
layoutThread = QThread::currentThread();
#endif
//setup instance of QTextLayout for all cases other than richtext
if (!richText) {
- QRect textRect = setupTextLayout();
+ qreal baseline = 0;
+ QRectF textRect = setupTextLayout(&naturalWidth, &baseline);
+
+ if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
+ return; // get this far we'll get a warning to that effect if it is.
+
layedOutTextRect = textRect;
size = textRect.size();
- dy -= size.height();
+ updateBaseline(baseline, q->height() - size.height());
} else {
singleline = false; // richtext can't elide or be optimized for single-line case
ensureDoc();
- doc->setDefaultFont(font);
+ extra->doc->setDefaultFont(font);
QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
if (rightToLeftText) {
if (horizontalAlignment == QQuickText::AlignLeft)
QTextOption option;
option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
option.setWrapMode(QTextOption::WrapMode(wrapMode));
- if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
- option.setUseDesignMetrics(true);
- doc->setDefaultTextOption(option);
+ option.setUseDesignMetrics(true);
+ extra->doc->setDefaultTextOption(option);
if (requireImplicitWidth && q->widthValid()) {
- doc->setTextWidth(-1);
- naturalWidth = doc->idealWidth();
+ extra->doc->setTextWidth(-1);
+ naturalWidth = extra->doc->idealWidth();
+ const bool wasInLayout = internalWidthUpdate;
+ internalWidthUpdate = true;
+ q->setImplicitWidth(naturalWidth);
+ internalWidthUpdate = wasInLayout;
}
+ if (internalWidthUpdate)
+ return;
if (wrapMode != QQuickText::NoWrap && q->widthValid())
- doc->setTextWidth(q->width());
+ extra->doc->setTextWidth(q->width());
else
- doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
- dy -= (int)doc->size().height();
- QSize dsize = doc->size().toSize();
- layedOutTextRect = QRect(QPoint(0,0), dsize);
- size = QSize(int(doc->idealWidth()),dsize.height());
- }
- int yoff = 0;
+ extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
+ QSizeF dsize = extra->doc->size();
+ layedOutTextRect = QRectF(QPointF(0,0), dsize);
+ size = QSizeF(extra->doc->idealWidth(),dsize.height());
- if (q->heightValid()) {
- if (vAlign == QQuickText::AlignBottom)
- yoff = dy;
- else if (vAlign == QQuickText::AlignVCenter)
- yoff = dy/2;
+ QFontMetricsF fm(font);
+ updateBaseline(fm.ascent(), q->height() - size.height());
}
- q->setBaselineOffset(fm.ascent() + yoff);
//### need to comfirm cost of always setting these for richText
internalWidthUpdate = true;
qreal iWidth = -1;
if (!q->widthValid())
iWidth = size.width();
- else if (requireImplicitWidth)
- iWidth = naturalWidth;
if (iWidth > -1)
q->setImplicitSize(iWidth, size.height());
internalWidthUpdate = false;
if (iWidth == -1)
q->setImplicitHeight(size.height());
- if (paintedSize != size) {
- paintedSize = size;
- emit q->paintedSizeChanged();
- }
+ if (layedOutTextRect.size() != previousSize)
+ emit q->contentSizeChanged();
updateType = UpdatePaintNode;
q->update();
}
bool QQuickTextPrivate::isLineLaidOutConnected()
{
- static int idx = this->signalIndex("lineLaidOut(QQuickTextLine*)");
- return this->isSignalConnected(idx);
+ Q_Q(QQuickText);
+ IS_SIGNAL_CONNECTED(q, "lineLaidOut(QQuickTextLine*)");
}
void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int lineOffset)
textLine->setWidth(q->width());
else
textLine->setWidth(INT_MAX);
- if (lineHeight != 1.0)
- textLine->setHeight((lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight);
+ if (lineHeight() != 1.0)
+ textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
emit q->lineLaidOut(textLine);
#endif
}
+void QQuickTextPrivate::elideFormats(
+ const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats)
+{
+ const int end = start + length;
+ QList<QTextLayout::FormatRange> formats = layout.additionalFormats();
+ for (int i = 0; i < formats.count(); ++i) {
+ QTextLayout::FormatRange format = formats.at(i);
+ const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
+ if (formatLength > 0) {
+ format.start = qMax(offset, format.start - start + offset);
+ format.length = formatLength;
+ elidedFormats->append(format);
+ }
+ }
+}
+
+QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine) const
+{
+ if (nextLine) {
+ nextLine->setLineWidth(INT_MAX);
+ return layout.engine()->elidedText(
+ Qt::TextElideMode(elideMode),
+ QFixed::fromReal(lineWidth),
+ 0,
+ line.textStart(),
+ line.textLength() + nextLine->textLength());
+ } else {
+ QString elideText = layout.text().mid(line.textStart(), line.textLength());
+ if (!styledText) {
+ // QFontMetrics won't help eliding styled text.
+ elideText[elideText.length() - 1] = elideChar;
+ // Appending the elide character may push the line over the maximum width
+ // in which case the elided text will need to be elided.
+ QFontMetricsF metrics(layout.font());
+ if (metrics.width(elideChar) + line.naturalTextWidth() >= lineWidth)
+ elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
+ }
+ return elideText;
+ }
+}
+
/*!
Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
already absolutely positioned horizontally).
*/
-QRect QQuickTextPrivate::setupTextLayout()
+QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth, qreal *const baseline)
{
Q_Q(QQuickText);
layout.setCacheEnabled(true);
QTextOption textOption = layout.textOption();
textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
- if (!cacheAllTextAsImage && !richTextAsImage && !disableDistanceField)
- textOption.setUseDesignMetrics(true);
+ textOption.setUseDesignMetrics(true);
layout.setTextOption(textOption);
layout.setFont(font);
- if ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
- || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight)) {
+ if (!requireImplicitWidth
+ && ((q->widthValid() && q->width() <= 0. && elideMode != QQuickText::ElideNone)
+ || (q->heightValid() && q->height() <= 0. && wrapMode != QQuickText::NoWrap && elideMode == QQuickText::ElideRight))) {
// we are elided and we have a zero width or height
if (!truncated) {
truncated = true;
emit q->lineCountChanged();
}
- if (requireImplicitWidth) {
- // Layout to determine the implicit width.
- layout.beginLayout();
-
- for (int i = 0; i < maximumLineCount; ++i) {
- QTextLine line = layout.createLine();
- if (!line.isValid())
- break;
- }
- layout.endLayout();
- naturalWidth = layout.maximumWidth();
- layout.clearLayout();
- }
-
- QFontMetrics fm(font);
- qreal height = (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : fm.height() * lineHeight;
- return QRect(0, 0, 0, height);
+ QFontMetricsF fm(font);
+ qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : fm.height() * lineHeight();
+ *baseline = fm.ascent();
+ return QRectF(0, 0, 0, height);
}
- const int lineWidth = q->widthValid() ? q->width() : INT_MAX;
+ qreal lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
+ const qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX;
+
const bool customLayout = isLineLaidOutConnected();
const bool wasTruncated = truncated;
- const bool singlelineElide = !styledText && elideMode != QQuickText::ElideNone && q->widthValid();
- const bool multilineElide = !styledText
- && elideMode == QQuickText::ElideRight
+ bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
+ bool multilineElide = elideMode == QQuickText::ElideRight
&& q->widthValid()
&& (q->heightValid() || maximumLineCountValid);
- const bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
+ bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
- const bool horizontalFit = fontSizeMode & QQuickText::HorizontalFit && q->widthValid();
- const bool verticalFit = fontSizeMode & QQuickText::VerticalFit
+ bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
+ bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
&& (q->heightValid() || (maximumLineCountValid && canWrap));
+
const bool pixelSize = font.pixelSize() != -1;
- const QString layoutText = layout.text();
+ QString layoutText = layout.text();
int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
- int smallFont = fontSizeMode != QQuickText::FixedSize
- ? qMin(pixelSize ? minimumPixelSize : minimumPointSize, largeFont)
+ int smallFont = fontSizeMode() != QQuickText::FixedSize
+ ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
: largeFont;
int scaledFontSize = largeFont;
qreal height = 0;
QString elideText;
bool once = true;
+ int elideStart = 0;
+ int elideEnd = 0;
- naturalWidth = 0;
+ *naturalWidth = 0;
- do {
+ int eos = multilengthEos;
+
+ // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
+ // doesn't fit within the element dimensions.
+ for (;;) {
if (!once) {
if (pixelSize)
scaledFont.setPixelSize(scaledFontSize);
scaledFont.setPointSize(scaledFontSize);
layout.setFont(scaledFont);
}
+
layout.beginLayout();
bool wrapped = false;
elide = false;
int characterCount = 0;
int unwrappedLineCount = 1;
+ int maxLineCount = maximumLineCount();
height = 0;
br = QRectF();
line = layout.createLine();
setLineGeometry(line, lineWidth, height);
}
- if (multilineElide && height > q->height() && visibleCount > 1) {
+ // Elide the previous line if the accumulated height of the text exceeds the height
+ // of the element.
+ if (multilineElide && height > maxHeight && visibleCount > 1) {
+ elide = true;
+ if (eos != -1) // There's an abbreviated string available, skip the rest as it's
+ break; // all going to be discarded.
+
truncated = true;
truncateHeight = true;
height = preLayoutHeight;
- elide = true;
characterCount = line.textStart() + line.textLength();
+ visibleCount -= 1;
+
+ QTextLine previousLine = layout.lineAt(visibleCount - 1);
+ elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
+ ? elidedText(lineWidth, previousLine, &line)
+ : elidedText(lineWidth, previousLine);
+ elideStart = previousLine.textStart();
+ // elideEnd isn't required for right eliding.
- QTextLine previousLine = layout.lineAt(visibleCount - 2);
- elideText = layoutText.mid(previousLine.textStart(), previousLine.textLength());
- if (layoutText.at(line.textStart() - 1) != QChar::LineSeparator) {
- line.setLineWidth(INT_MAX);
- elideText += layoutText.mid(line.textStart(), line.textLength());
- } else {
- elideText[elideText.length() - 1] = elideChar;
- }
- line.setLineWidth(0);
- line.setPosition(QPointF(FLT_MAX, FLT_MAX));
line = previousLine;
- --visibleCount;
- height -= (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : previousLine.height() * lineHeight;
+ height -= (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : previousLine.height() * lineHeight();
break;
}
if (!nextLine.isValid()) {
characterCount = line.textStart() + line.textLength();
if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > lineWidth) {
+ // Elide a single line of text if its width exceeds the element width.
+ elide = true;
+ if (eos != -1) // There's an abbreviated string available.
+ break;
+
truncated = true;
height = preLayoutHeight;
- elide = true;
- elideText = layoutText.mid(line.textStart(), line.textLength());
+ elideText = layout.engine()->elidedText(
+ Qt::TextElideMode(elideMode),
+ QFixed::fromReal(lineWidth),
+ 0,
+ line.textStart(),
+ line.textLength());
+ elideStart = line.textStart();
+ elideEnd = elideStart + line.textLength();
} else {
br = br.united(line.naturalTextRect());
}
if (!wrappedLine)
++unwrappedLineCount;
- if (visibleCount == maximumLineCount) {
+ // Stop if the maximum number of lines has been reached and elide the last line
+ // if enabled.
+ if (visibleCount == maxLineCount) {
truncated = true;
characterCount = nextLine.textStart() + nextLine.textLength();
if (multilineElide) {
- height = preLayoutHeight;
elide = true;
- elideText = layoutText.mid(line.textStart(), line.textLength());
- if (wrappedLine) {
- nextLine.setLineWidth(INT_MAX);
- elideText += layoutText.mid(nextLine.textStart(), nextLine.textLength());
- } else {
- elideText[elideText.length() - 1] = elideChar;
- }
- elideText += elideChar;
+ if (eos != -1) // There's an abbreviated string available
+ break;
+ height = preLayoutHeight;
+ elideText = wrappedLine
+ ? elidedText(lineWidth, line, &nextLine)
+ : elidedText(lineWidth, line);
+ elideStart = line.textStart();
+ // elideEnd isn't required for right eliding.
} else {
br = br.united(line.naturalTextRect());
}
- nextLine.setLineWidth(0);
- nextLine.setPosition(QPointF(FLT_MAX, FLT_MAX));
break;
}
}
br = br.united(line.naturalTextRect());
line = nextLine;
}
-
layout.endLayout();
+ br.moveTop(0);
+ // Save the implicitWidth of the text on the first layout only.
if (once) {
- naturalWidth = layout.maximumWidth();
+ *naturalWidth = layout.maximumWidth();
once = false;
if (requireImplicitWidth
&& characterCount < layoutText.length()
- && unwrappedLineCount < maximumLineCount) {
+ && unwrappedLineCount < maxLineCount) {
// Use a new layout to get the maximum width for the remaining text. Using a
// different layout excludes the truncated text from rendering.
QTextLayout widthLayout(layoutText.mid(characterCount), scaledFont);
widthLayout.setTextOption(layout.textOption());
- for (; unwrappedLineCount <= maximumLineCount; ++unwrappedLineCount) {
+ widthLayout.beginLayout();
+ for (; unwrappedLineCount <= maxLineCount; ++unwrappedLineCount) {
QTextLine line = widthLayout.createLine();
if (!line.isValid())
break;
}
- naturalWidth = qMax(naturalWidth, widthLayout.maximumWidth());
+ widthLayout.endLayout();
+ *naturalWidth = qMax(*naturalWidth, widthLayout.maximumWidth());
}
+
+ bool wasInLayout = internalWidthUpdate;
+ internalWidthUpdate = true;
+ q->setImplicitWidth(*naturalWidth);
+ internalWidthUpdate = wasInLayout;
+
+ singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
+ multilineElide = elideMode == QQuickText::ElideRight
+ && q->widthValid()
+ && (q->heightValid() || maximumLineCountValid);
+ canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
+
+ horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
+ verticalFit = fontSizeMode() & QQuickText::VerticalFit
+ && (q->heightValid() || (maximumLineCountValid && canWrap));
+
+ const qreal oldWidth = lineWidth;
+ lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX;
+ if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit))
+ continue;
+ }
+
+ // If the next needs to be elided and there's an abbreviated string available
+ // go back and do another layout with the abbreviated string.
+ if (eos != -1 && elide) {
+ int start = eos + 1;
+ eos = text.indexOf(QLatin1Char('\x9c'), start);
+ layoutText = text.mid(start, eos != -1 ? eos - start : -1);
+ layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ layout.setText(layoutText);
+ textHasChanged = true;
+ continue;
}
+ if (!horizontalFit && !verticalFit)
+ break;
+
+ // Try and find a font size that better fits the dimensions of the element.
QRectF unelidedRect = br.united(line.naturalTextRect());
if (horizontalFit) {
if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
largeFont = scaledFontSize - 1;
- scaledFontSize = (smallFont + largeFont) / 2;
if (smallFont > largeFont)
break;
+ scaledFontSize = (smallFont + largeFont) / 2;
+ if (pixelSize)
+ scaledFont.setPixelSize(scaledFontSize);
+ else
+ scaledFont.setPointSize(scaledFontSize);
continue;
} else if (!verticalFit) {
smallFont = scaledFontSize;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont == largeFont)
break;
+ scaledFontSize = (smallFont + largeFont + 1) / 2;
}
}
if (verticalFit) {
- if (truncateHeight || (q->heightValid() && unelidedRect.height() > q->height())) {
+ if (truncateHeight || unelidedRect.height() > maxHeight) {
largeFont = scaledFontSize - 1;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont > largeFont)
break;
+ scaledFontSize = (smallFont + largeFont) / 2;
+
} else {
smallFont = scaledFontSize;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
if (smallFont == largeFont)
break;
+ scaledFontSize = (smallFont + largeFont + 1) / 2;
}
}
- } while (horizontalFit || verticalFit);
+ }
+
+ if (eos != multilengthEos)
+ truncated = true;
if (elide) {
- if (!elideLayout)
+ if (!elideLayout) {
elideLayout = new QTextLayout;
+ elideLayout->setCacheEnabled(true);
+ }
+ if (styledText) {
+ QList<QTextLayout::FormatRange> formats;
+ switch (elideMode) {
+ case QQuickText::ElideRight:
+ elideFormats(elideStart, elideText.length() - 1, 0, &formats);
+ break;
+ case QQuickText::ElideLeft:
+ elideFormats(elideEnd - elideText.length() + 1, elideText.length() - 1, 1, &formats);
+ break;
+ case QQuickText::ElideMiddle: {
+ const int index = elideText.indexOf(elideChar);
+ if (index != -1) {
+ elideFormats(elideStart, index, 0, &formats);
+ elideFormats(
+ elideEnd - elideText.length() + index + 1,
+ elideText.length() - index - 1,
+ index + 1,
+ &formats);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ elideLayout->setAdditionalFormats(formats);
+ }
+
elideLayout->setFont(layout.font());
elideLayout->setTextOption(layout.textOption());
elideLayout->setText(elideText);
- elideLayout->setText(elideLayout->engine()->elidedText(Qt::TextElideMode(elideMode), lineWidth));
elideLayout->beginLayout();
QTextLine elidedLine = elideLayout->createLine();
br = br.united(elidedLine.naturalTextRect());
- if (visibleCount > 1)
- line.setPosition(QPointF(FLT_MAX, FLT_MAX));
- else
+ if (visibleCount == 1)
layout.clearLayout();
} else {
delete elideLayout;
elideLayout = 0;
}
+ QTextLine firstLine = visibleCount == 1 && elideLayout
+ ? elideLayout->lineAt(0)
+ : layout.lineAt(0);
+ Q_ASSERT(firstLine.isValid());
+ *baseline = firstLine.y() + firstLine.ascent();
+
if (!customLayout)
br.setHeight(height);
if (truncated != wasTruncated)
emit q->truncatedChanged();
- return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
+ return br;
}
void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
if (imgTags.isEmpty()) {
line.setPosition(QPointF(line.position().x(), height));
- height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : line.height() * lineHeight;
+ height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
return;
}
qreal textHeight = line.height();
qreal totalLineHeight = textHeight;
- QList<QDeclarativeStyledTextImgTag *> imagesInLine;
+ QList<QQuickStyledTextImgTag *> imagesInLine;
- foreach (QDeclarativeStyledTextImgTag *image, imgTags) {
+ foreach (QQuickStyledTextImgTag *image, imgTags) {
if (image->position >= line.textStart() &&
image->position < line.textStart() + line.textLength()) {
if (!image->pix) {
- QUrl url = qmlContext(q)->resolvedUrl(image->url);
- image->pix = new QDeclarativePixmap(qmlEngine(q), url, image->size);
+ QUrl url = q->baseUrl().resolved(image->url);
+ image->pix = new QQuickPixmap(qmlEngine(q), url, image->size);
if (image->pix->isLoading()) {
image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
- nbActiveDownloads++;
+ if (!extra.isAllocated() || !extra->nbActiveDownloads)
+ extra.value().nbActiveDownloads = 0;
+ extra->nbActiveDownloads++;
} else if (image->pix->isReady()) {
if (!image->size.isValid()) {
image->size = image->pix->implicitSize();
}
qreal ih = qreal(image->size.height());
- if (image->align == QDeclarativeStyledTextImgTag::Top)
+ if (image->align == QQuickStyledTextImgTag::Top)
image->pos.setY(0);
- else if (image->align == QDeclarativeStyledTextImgTag::Middle)
+ else if (image->align == QQuickStyledTextImgTag::Middle)
image->pos.setY((textHeight / 2.0) - (ih / 2.0));
else
image->pos.setY(textHeight - ih);
}
}
- foreach (QDeclarativeStyledTextImgTag *image, imagesInLine) {
+ foreach (QQuickStyledTextImgTag *image, imagesInLine) {
totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
image->pos.setX(line.cursorToX(image->position));
image->pos.setY(image->pos.y() + height + textTop);
}
line.setPosition(QPointF(line.position().x(), height + textTop));
- height += (lineHeightMode == QQuickText::FixedHeight) ? lineHeight : totalLineHeight * lineHeight;
-}
-
-/*!
- Returns a painted version of the QQuickTextPrivate::layout QTextLayout.
- If \a drawStyle is true, the style color overrides all colors in the document.
-*/
-QPixmap QQuickTextPrivate::textLayoutImage(bool drawStyle)
-{
- QSize size = layedOutTextRect.size();
-
- //paint text
- QPixmap img(size);
- if (!size.isEmpty()) {
- img.fill(Qt::transparent);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
- bool oldSmooth = qt_applefontsmoothing_enabled;
- qt_applefontsmoothing_enabled = false;
-#endif*/
- QPainter p(&img);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
- qt_applefontsmoothing_enabled = oldSmooth;
-#endif*/
- drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
- }
- return img;
-}
-
-/*!
- Paints the QQuickTextPrivate::layout QTextLayout into \a painter at \a pos. If
- \a drawStyle is true, the style color overrides all colors in the document.
-*/
-void QQuickTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
-{
- if (drawStyle)
- painter->setPen(styleColor);
- else
- painter->setPen(color);
- painter->setFont(font);
- layout.draw(painter, pos);
- if (!elidePos.isNull())
- painter->drawText(pos + elidePos, elideChar);
-}
-
-/*!
- Returns a painted version of the QQuickTextPrivate::doc QTextDocument.
- If \a drawStyle is true, the style color overrides all colors in the document.
-*/
-QPixmap QQuickTextPrivate::textDocumentImage(bool drawStyle)
-{
- QSize size = doc->size().toSize();
-
- //paint text
- QPixmap img(size);
- img.fill(Qt::transparent);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
- bool oldSmooth = qt_applefontsmoothing_enabled;
- qt_applefontsmoothing_enabled = false;
-#endif*/
- QPainter p(&img);
-/*#ifdef Q_OS_MAC // Fails on CocoaX64
- qt_applefontsmoothing_enabled = oldSmooth;
-#endif*/
-
- QAbstractTextDocumentLayout::PaintContext context;
-
- QTextOption oldOption(doc->defaultTextOption());
- if (drawStyle) {
- context.palette.setColor(QPalette::Text, styleColor);
- QTextOption colorOption(doc->defaultTextOption());
- colorOption.setFlags(QTextOption::SuppressColors);
- doc->setDefaultTextOption(colorOption);
- } else {
- context.palette.setColor(QPalette::Text, color);
- }
- doc->documentLayout()->draw(&p, context);
- if (drawStyle)
- doc->setDefaultTextOption(oldOption);
- return img;
-}
-
-
-void QQuickTextPrivate::markDirty()
-{
- Q_Q(QQuickText);
- if (!invalidateImageCache() && q->isComponentComplete()) {
- updateType = UpdatePaintNode;
- q->update();
- }
-}
-
-/*!
- Mark the image cache as dirty.
-*/
-bool QQuickTextPrivate::invalidateImageCache()
-{
- Q_Q(QQuickText);
-
- if (richTextAsImage || cacheAllTextAsImage || (disableDistanceField && style != QQuickText::Normal)) { // If actually using the image cache
- if (imageCacheDirty)
- return true;
-
- imageCacheDirty = true;
-
- if (q->isComponentComplete())
- QCoreApplication::postEvent(q, new QEvent(QEvent::User));
- return true;
- }
-
- return false;
-}
-
-/*!
- Tests if the image cache is dirty, and repaints it if it is.
-*/
-void QQuickTextPrivate::checkImageCache()
-{
- Q_Q(QQuickText);
-
- if (!imageCacheDirty)
- return;
-
- if (text.isEmpty()) {
-
- delete imageCache;
- imageCache = 0;
-
- } else {
-
- QPixmap textImage;
- QPixmap styledImage;
-
- if (richText) {
- textImage = textDocumentImage(false);
- if (style != QQuickText::Normal)
- styledImage = textDocumentImage(true); //### should use styleColor
- } else {
- textImage = textLayoutImage(false);
- if (style != QQuickText::Normal)
- styledImage = textLayoutImage(true); //### should use styleColor
- }
-
- delete imageCache;
- switch (style) {
- case QQuickText::Outline:
- imageCache = new QPixmap(drawOutline(textImage, styledImage));
- break;
- case QQuickText::Sunken:
- imageCache = new QPixmap(drawOutline(textImage, styledImage, -1));
- break;
- case QQuickText::Raised:
- imageCache = new QPixmap(drawOutline(textImage, styledImage, 1));
- break;
- default:
- imageCache = new QPixmap(textImage);
- break;
- }
-
- }
-
- imageCacheDirty = false;
- textureImageCacheDirty = true;
- updateType = UpdatePaintNode;
- q->update();
+ height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
}
/*!
*/
void QQuickTextPrivate::ensureDoc()
{
- if (!doc) {
+ if (!extra.isAllocated() || !extra->doc) {
Q_Q(QQuickText);
- doc = new QQuickTextDocumentWithImageResources(q);
- doc->setDocumentMargin(0);
- doc->setBaseUrl(q->baseUrl());
- FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
+ extra.value().doc = new QQuickTextDocumentWithImageResources(q);
+ extra->doc->setDocumentMargin(0);
+ extra->doc->setBaseUrl(q->baseUrl());
+ FAST_CONNECT(extra->doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
}
}
/*!
- Draw \a styleSource as an outline around \a source and return the new image.
-*/
-QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
-{
- QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
- img.fill(Qt::transparent);
-
- QPainter ppm(&img);
-
- QPoint pos(0, 0);
- pos += QPoint(-1, 0);
- ppm.drawPixmap(pos, styleSource);
- pos += QPoint(2, 0);
- ppm.drawPixmap(pos, styleSource);
- pos += QPoint(-1, -1);
- ppm.drawPixmap(pos, styleSource);
- pos += QPoint(0, 2);
- ppm.drawPixmap(pos, styleSource);
-
- pos += QPoint(0, -1);
- ppm.drawPixmap(pos, source);
- ppm.end();
-
- return img;
-}
-
-/*!
- Draw \a styleSource below \a source at \a yOffset and return the new image.
-*/
-QPixmap QQuickTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
-{
- QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
- img.fill(Qt::transparent);
-
- QPainter ppm(&img);
-
- ppm.drawPixmap(QPoint(0, yOffset), styleSource);
- ppm.drawPixmap(0, 0, source);
-
- ppm.end();
-
- return img;
-}
-
-/*!
\qmlclass Text QQuickText
\inqmlmodule QtQuick 2
\ingroup qml-basic-visual-elements
The properties of a line are:
\list
- \o number (read-only)
- \o x
- \o y
- \o width
- \o height
+ \li number (read-only)
+ \li x
+ \li y
+ \li width
+ \li height
\endlist
For example, this will move the first 5 lines of a text element by 100 pixels to the right:
The link must be in rich text or HTML format and the
\a link string provides access to the particular link.
- \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
+ \snippet doc/src/snippets/qml/text/onLinkActivated.qml 0
The example code will display the text
"The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
The weight can be one of:
\list
- \o Font.Light
- \o Font.Normal - the default
- \o Font.DemiBold
- \o Font.Bold
- \o Font.Black
+ \li Font.Light
+ \li Font.Normal - the default
+ \li Font.DemiBold
+ \li Font.Bold
+ \li Font.Black
\endlist
\qml
Sets the capitalization for the text.
\list
- \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
- \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
- \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
- \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
- \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
\endlist
\qml
d->font.setPointSizeF(size/2.0);
}
- if (oldFont != d->font)
+ if (oldFont != d->font) {
+ // if the format changes the size of the text
+ // with headings or <font> tag, we need to re-parse
+ if (d->formatModifiesFontSize)
+ d->textHasChanged = true;
d->updateLayout();
+ }
emit fontChanged(d->sourceFont);
}
if (isComponentComplete()) {
if (d->richText) {
d->ensureDoc();
- d->doc->setText(n);
- d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
- d->richTextAsImage = enableImageCache();
+ d->extra->doc->setText(n);
+ d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
d->rightToLeftText = d->text.isRightToLeft();
}
QColor QQuickText::color() const
{
Q_D(const QQuickText);
- return d->color;
+ return QColor::fromRgba(d->color);
}
void QQuickText::setColor(const QColor &color)
{
Q_D(QQuickText);
- if (d->color == color)
+ QRgb rgb = color.rgba();
+ if (d->color == rgb)
return;
- d->color = color;
- d->markDirty();
- emit colorChanged(d->color);
+ d->color = rgb;
+ if (isComponentComplete()) {
+ d->updateType = QQuickTextPrivate::UpdatePaintNode;
+ update();
+ }
+ emit colorChanged();
}
+
+/*!
+ \qmlproperty color QtQuick2::Text::linkColor
+
+ The color of links in the text.
+
+ This property works with the StyledText \l textFormat, but not with RichText.
+ Link color in RichText can be specified by including CSS style tags in the
+ text.
+*/
+
+QColor QQuickText::linkColor() const
+{
+ Q_D(const QQuickText);
+ return QColor::fromRgba(d->linkColor);
+}
+
+void QQuickText::setLinkColor(const QColor &color)
+{
+ Q_D(QQuickText);
+ QRgb rgb = color.rgba();
+ if (d->linkColor == rgb)
+ return;
+
+ d->linkColor = rgb;
+ update();
+ emit linkColorChanged();
+}
+
/*!
\qmlproperty enumeration QtQuick2::Text::style
Supported text styles are:
\list
- \o Text.Normal - the default
- \o Text.Outline
- \o Text.Raised
- \o Text.Sunken
+ \li Text.Normal - the default
+ \li Text.Outline
+ \li Text.Raised
+ \li Text.Sunken
\endlist
\qml
if (d->style == style)
return;
- // changing to/from Normal requires the boundingRect() to change
- if (isComponentComplete() && (d->style == Normal || style == Normal)) {
+ d->style = style;
+ if (isComponentComplete()) {
d->updateType = QQuickTextPrivate::UpdatePaintNode;
update();
}
- d->style = style;
- d->markDirty();
emit styleChanged(d->style);
}
QColor QQuickText::styleColor() const
{
Q_D(const QQuickText);
- return d->styleColor;
+ return QColor::fromRgba(d->styleColor);
}
void QQuickText::setStyleColor(const QColor &color)
{
Q_D(QQuickText);
- if (d->styleColor == color)
+ QRgb rgb = color.rgba();
+ if (d->styleColor == rgb)
return;
- d->styleColor = color;
- d->markDirty();
- emit styleColorChanged(d->styleColor);
+ d->styleColor = rgb;
+ if (isComponentComplete()) {
+ d->updateType = QQuickTextPrivate::UpdatePaintNode;
+ update();
+ }
+ emit styleColorChanged();
}
/*!
bool QQuickTextPrivate::determineHorizontalAlignment()
{
if (hAlignImplicit) {
- bool alignToRight = text.isEmpty() ? qApp->inputPanel()->inputDirection() == Qt::RightToLeft : rightToLeftText;
+ bool alignToRight = text.isEmpty() ? qApp->inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
return setHAlign(alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
}
return false;
}
}
-QTextDocument *QQuickTextPrivate::textDocument()
-{
- return doc;
-}
-
QQuickText::VAlignment QQuickText::vAlign() const
{
Q_D(const QQuickText);
wrap if an explicit width has been set. wrapMode can be one of:
\list
- \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
- \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
- \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
- \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
+ \li Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l contentWidth will exceed a set width.
+ \li Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l contentWidth will exceed a set width.
+ \li Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \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.
\endlist
*/
QQuickText::WrapMode QQuickText::wrapMode() const
int QQuickText::maximumLineCount() const
{
Q_D(const QQuickText);
- return d->maximumLineCount;
+ return d->maximumLineCount();
}
void QQuickText::setMaximumLineCount(int lines)
Q_D(QQuickText);
d->maximumLineCountValid = lines==INT_MAX ? false : true;
- if (d->maximumLineCount != lines) {
- d->maximumLineCount = lines;
+ if (d->maximumLineCount() != lines) {
+ d->extra.value().maximumLineCount = lines;
d->updateLayout();
emit maximumLineCountChanged();
}
{
Q_D(QQuickText);
setMaximumLineCount(INT_MAX);
- d->elidePos = QPointF();
if (d->truncated != false) {
d->truncated = false;
emit truncatedChanged();
Supported text formats are:
\list
- \o Text.AutoText (default)
- \o Text.PlainText
- \o Text.StyledText
- \o Text.RichText
+ \li Text.AutoText (default)
+ \li Text.PlainText
+ \li Text.StyledText
+ \li Text.RichText
\endlist
If the text format is \c Text.AutoText the text element
\table
\row
- \o
+ \li
\qml
Column {
Text {
}
}
\endqml
- \o \image declarative-textformat.png
+ \li \image declarative-textformat.png
\endtable
*/
QQuickText::TextFormat QQuickText::textFormat() const
if (isComponentComplete()) {
if (!wasRich && d->richText) {
d->ensureDoc();
- d->doc->setText(d->text);
- d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
- d->richTextAsImage = enableImageCache();
+ d->extra->doc->setText(d->text);
+ d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
d->rightToLeftText = d->text.isRightToLeft();
}
Eliding can be:
\list
- \o Text.ElideNone - the default
- \o Text.ElideLeft
- \o Text.ElideMiddle
- \o Text.ElideRight
+ \li Text.ElideNone - the default
+ \li Text.ElideLeft
+ \li Text.ElideMiddle
+ \li Text.ElideRight
\endlist
If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
d->elideMode = mode;
d->updateLayout();
- emit elideModeChanged(d->elideMode);
+ emit elideModeChanged(mode);
}
/*!
This property specifies a base URL which is used to resolve relative URLs
within the text.
+ Urls are resolved to be within the same directory as the target of the base
+ URL meaning any portion of the path after the last '/' will be ignored.
+
+ \table
+ \header \li Base URL \li Relative URL \li Resolved URL
+ \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
+ \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
+ \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
+ \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
+ \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
+ \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
+ \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
+ \endtable
+
By default is the url of the Text element.
*/
{
Q_D(const QQuickText);
if (d->baseUrl.isEmpty()) {
- if (QDeclarativeContext *context = qmlContext(this))
+ if (QQmlContext *context = qmlContext(this))
const_cast<QQuickTextPrivate *>(d)->baseUrl = context->baseUrl();
}
return d->baseUrl;
if (baseUrl() != url) {
d->baseUrl = url;
- if (d->doc)
- d->doc->setBaseUrl(url);
+ if (d->richText) {
+ d->ensureDoc();
+ d->extra->doc->setBaseUrl(url);
+ }
+ if (d->styledText) {
+ d->textHasChanged = true;
+ qDeleteAll(d->imgTags);
+ d->imgTags.clear();
+ d->updateLayout();
+ }
emit baseUrlChanged();
}
}
void QQuickText::resetBaseUrl()
{
- if (QDeclarativeContext *context = qmlContext(this))
+ if (QQmlContext *context = qmlContext(this))
setBaseUrl(context->baseUrl());
else
setBaseUrl(QUrl());
{
Q_D(const QQuickText);
- QRect rect = d->layedOutTextRect;
+ QRectF rect = d->layedOutTextRect;
if (d->style != Normal)
rect.adjust(-1, 0, 1, 2);
// Could include font max left/right bearings to either side of rectangle.
- int h = height();
+ qreal w = width();
+ switch (d->hAlign) {
+ case AlignLeft:
+ case AlignJustify:
+ break;
+ case AlignRight:
+ rect.moveLeft(w - rect.width());
+ break;
+ case AlignHCenter:
+ rect.moveLeft((w - rect.width()) / 2);
+ break;
+ }
+
+ qreal h = height();
switch (d->vAlign) {
case AlignTop:
break;
break;
}
- return QRectF(rect);
+ return rect;
}
/*! \internal */
bool leftAligned = effectiveHAlign() == QQuickText::AlignLeft;
bool wrapped = d->wrapMode != QQuickText::NoWrap;
bool elide = d->elideMode != QQuickText::ElideNone;
- bool scaleFont = d->fontSizeMode != QQuickText::FixedSize && (widthValid() || heightValid());
+ bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
goto geomChangeDone;
if (d->elideMode == QQuickText::ElideRight && wrapped && newGeometry.height() > oldGeometry.height() && !scaleFont) {
if (!d->truncated)
goto geomChangeDone; // Multiline eliding not affected if we're not currently truncated and we get higher.
- if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount)
+ if (d->maximumLineCountValid && d->lineCount == d->maximumLineCount())
goto geomChangeDone; // Multiline eliding not affected if we're already at max line count and we get higher.
}
- if (d->updateOnComponentComplete) {
+ if (d->updateOnComponentComplete || d->textHasChanged) {
// We need to re-elide
d->updateLayout();
} else {
d->updateLayout();
#endif
- // XXX todo - some styled text can be done by the QQuickTextNode
- if (d->richTextAsImage || d->cacheAllTextAsImage || (d->disableDistanceField && d->style != Normal)) {
- bool wasDirty = d->textureImageCacheDirty;
- d->textureImageCacheDirty = false;
-
- if (!d->imageCache || d->imageCache->isNull()) {
- delete oldNode;
- return 0;
- }
-
- QSGImageNode *node = 0;
- if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsTexture) {
- delete oldNode;
- node = QQuickItemPrivate::get(this)->sceneGraphContext()->createImageNode();
- d->texture = new QSGPlainTexture();
- wasDirty = true;
- d->nodeType = QQuickTextPrivate::NodeIsTexture;
- } else {
- node = static_cast<QSGImageNode *>(oldNode);
- Q_ASSERT(d->texture);
- }
-
- if (wasDirty) {
- qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache->toImage());
- node->setTexture(0);
- node->setTexture(d->texture);
- }
-
- node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache->width(), d->imageCache->height()));
- node->setSourceRect(QRectF(0, 0, 1, 1));
- node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
- node->setVerticalWrapMode(QSGTexture::ClampToEdge);
- node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
- node->update();
-
- return node;
-
+ QQuickTextNode *node = 0;
+ if (!oldNode) {
+ node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
} else {
- QQuickTextNode *node = 0;
- if (!oldNode || d->nodeType != QQuickTextPrivate::NodeIsText) {
- delete oldNode;
- node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext(), this);
- d->nodeType = QQuickTextPrivate::NodeIsText;
- } else {
- node = static_cast<QQuickTextNode *>(oldNode);
- }
-
- node->deleteContent();
- node->setMatrix(QMatrix4x4());
-
- if (d->richText) {
- d->ensureDoc();
- node->addTextDocument(bounds.topLeft(), d->doc, d->color, d->style, d->styleColor);
-
- } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
- node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
- if (d->elideLayout)
- node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, d->color, d->style, d->styleColor);
- }
+ node = static_cast<QQuickTextNode *>(oldNode);
+ }
- foreach (QDeclarativeStyledTextImgTag *img, d->visibleImgTags) {
- QDeclarativePixmap *pix = img->pix;
- if (pix && pix->isReady())
- node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
+ node->deleteContent();
+ node->setMatrix(QMatrix4x4());
+
+ const QColor color = QColor::fromRgba(d->color);
+ const QColor styleColor = QColor::fromRgba(d->styleColor);
+ const QColor linkColor = QColor::fromRgba(d->linkColor);
+
+ if (d->richText) {
+ d->ensureDoc();
+ node->addTextDocument(bounds.topLeft(), d->extra->doc, color, d->style, styleColor, linkColor);
+ } else if (d->elideMode == QQuickText::ElideNone || bounds.width() > 0.) {
+ int unelidedLineCount = d->lineCount;
+ if (d->elideLayout)
+ unelidedLineCount -= 1;
+ if (unelidedLineCount > 0) {
+ node->addTextLayout(
+ QPoint(0, bounds.y()),
+ &d->layout,
+ d->color, d->style, d->styleColor, d->linkColor,
+ QColor(), QColor(), -1, -1,
+ 0, unelidedLineCount);
}
- return node;
+ if (d->elideLayout)
+ node->addTextLayout(QPoint(0, bounds.y()), d->elideLayout, color, d->style, styleColor, linkColor);
}
-}
-bool QQuickText::event(QEvent *e)
-{
- Q_D(QQuickText);
- if (e->type() == QEvent::User) {
- d->checkImageCache();
- return true;
- } else {
- return QQuickImplicitSizeItem::event(e);
+ foreach (QQuickStyledTextImgTag *img, d->visibleImgTags) {
+ QQuickPixmap *pix = img->pix;
+ if (pix && pix->isReady())
+ node->addImage(QRectF(img->pos.x(), img->pos.y() + bounds.y(), pix->width(), pix->height()), pix->image());
}
+ return node;
}
void QQuickText::updatePolish()
{
Q_D(QQuickText);
- if (d->updateLayoutOnPolish)
- d->updateLayout();
- else
- d->updateSize();
+ d->updateSize();
}
/*!
- \qmlproperty real QtQuick2::Text::paintedWidth
+ \qmlproperty real QtQuick2::Text::contentWidth
Returns the width of the text, including width past the width
which is covered due to insufficient wrapping if WrapMode is set.
*/
-qreal QQuickText::paintedWidth() const
+qreal QQuickText::contentWidth() const
{
Q_D(const QQuickText);
- return d->paintedSize.width();
+ return d->layedOutTextRect.width();
}
/*!
- \qmlproperty real QtQuick2::Text::paintedHeight
+ \qmlproperty real QtQuick2::Text::contentHeight
Returns the height of the text, including height past the height
which is covered due to there being more text than fits in the set height.
*/
-qreal QQuickText::paintedHeight() const
+qreal QQuickText::contentHeight() const
{
Q_D(const QQuickText);
- return d->paintedSize.height();
+ return d->layedOutTextRect.height();
}
/*!
qreal QQuickText::lineHeight() const
{
Q_D(const QQuickText);
- return d->lineHeight;
+ return d->lineHeight();
}
void QQuickText::setLineHeight(qreal lineHeight)
{
Q_D(QQuickText);
- if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
+ if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
return;
- d->lineHeight = lineHeight;
+ d->extra.value().lineHeight = lineHeight;
d->updateLayout();
emit lineHeightChanged(lineHeight);
}
The possible values are:
\list
- \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
+ \li Text.ProportionalHeight (default) - this sets the spacing proportional to the
line (as a multiplier). For example, set to 2 for double spacing.
- \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
+ \li Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
\endlist
*/
QQuickText::LineHeightMode QQuickText::lineHeightMode() const
{
Q_D(const QQuickText);
- return d->lineHeightMode;
+ return d->lineHeightMode();
}
void QQuickText::setLineHeightMode(LineHeightMode mode)
{
Q_D(QQuickText);
- if (mode == d->lineHeightMode)
+ if (mode == d->lineHeightMode())
return;
- d->lineHeightMode = mode;
+ d->extra.value().lineHeightMode = mode;
d->updateLayout();
emit lineHeightModeChanged(mode);
The possible values are:
\list
- \o Text.FixedSize (default) - The size specified by \l font.pixelSize
+ \li Text.FixedSize (default) - The size specified by \l font.pixelSize
or \l font.pointSize is used.
- \o Text.HorizontalFit - The largest size up to the size specified that fits
+ \li Text.HorizontalFit - The largest size up to the size specified that fits
within the width of the item without wrapping is used.
- \o Text.VerticalFit - The largest size up to the size specified that fits
+ \li Text.VerticalFit - The largest size up to the size specified that fits
the height of the item is used.
- \o Text.Fit - The largest size up to the size specified the fits within the
+ \li Text.Fit - The largest size up to the size specified the fits within the
width and height of the item is used.
\endlist
QQuickText::FontSizeMode QQuickText::fontSizeMode() const
{
Q_D(const QQuickText);
- return d->fontSizeMode;
+ return d->fontSizeMode();
}
void QQuickText::setFontSizeMode(FontSizeMode mode)
{
Q_D(QQuickText);
- if (d->fontSizeMode == mode)
+ if (d->fontSizeMode() == mode)
return;
polish();
- d->fontSizeMode = mode;
+ d->extra.value().fontSizeMode = mode;
emit fontSizeModeChanged();
}
int QQuickText::minimumPixelSize() const
{
Q_D(const QQuickText);
- return d->minimumPixelSize;
+ return d->minimumPixelSize();
}
void QQuickText::setMinimumPixelSize(int size)
{
Q_D(QQuickText);
- if (d->minimumPixelSize == size)
+ if (d->minimumPixelSize() == size)
return;
- if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
+ if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
polish();
- d->minimumPixelSize = size;
+ d->extra.value().minimumPixelSize = size;
emit minimumPixelSizeChanged();
}
int QQuickText::minimumPointSize() const
{
Q_D(const QQuickText);
- return d->minimumPointSize;
+ return d->minimumPointSize();
}
void QQuickText::setMinimumPointSize(int size)
{
Q_D(QQuickText);
- if (d->minimumPointSize == size)
+ if (d->minimumPointSize() == size)
return;
- if (d->fontSizeMode != FixedSize && (widthValid() || heightValid()))
+ if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid()))
polish();
- d->minimumPointSize = size;
+ d->extra.value().minimumPointSize = size;
emit minimumPointSizeChanged();
}
int QQuickText::resourcesLoading() const
{
Q_D(const QQuickText);
- return d->doc ? d->doc->resourcesLoading() : 0;
+ if (d->richText && d->extra.isAllocated() && d->extra->doc)
+ return d->extra->doc->resourcesLoading();
+ return 0;
}
/*! \internal */
if (d->updateOnComponentComplete) {
if (d->richText) {
d->ensureDoc();
- d->doc->setText(d->text);
- d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
- d->richTextAsImage = enableImageCache();
+ d->extra->doc->setText(d->text);
+ d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
d->rightToLeftText = d->text.isRightToLeft();
}
QQuickItem::componentComplete();
if (d->updateOnComponentComplete)
d->updateLayout();
-
- // Enable accessibility for text items.
- d->setAccessibleFlagAndListener();
}
bool QQuickTextPrivate::isLinkActivatedConnected()
{
- static int idx = this->signalIndex("linkActivated(QString)");
- return this->isSignalConnected(idx);
+ Q_Q(QQuickText);
+ IS_SIGNAL_CONNECTED(q, "linkActivated(QString)");
}
/*! \internal */
{
Q_D(QQuickText);
+ QString link;
if (d->isLinkActivatedConnected()) {
if (d->styledText)
- d->activeLink = d->anchorAt(event->localPos());
- else if (d->richText && d->doc)
- d->activeLink = d->doc->documentLayout()->anchorAt(event->localPos());
+ link = d->anchorAt(event->localPos());
+ else if (d->richText) {
+ d->ensureDoc();
+ link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
+ }
}
- if (d->activeLink.isEmpty())
+ if (link.isEmpty()) {
event->setAccepted(false);
+ } else {
+ d->extra.value().activeLink = link;
+ }
// ### may malfunction if two of the same links are clicked & dragged onto each other)
if (d->isLinkActivatedConnected()) {
if (d->styledText)
link = d->anchorAt(event->localPos());
- else if (d->richText && d->doc)
- link = d->doc->documentLayout()->anchorAt(event->localPos());
+ else if (d->richText) {
+ d->ensureDoc();
+ link = d->extra->doc->documentLayout()->anchorAt(event->localPos());
+ }
}
- if (!link.isEmpty() && d->activeLink == link)
- emit linkActivated(d->activeLink);
+ if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
+ emit linkActivated(d->extra->activeLink);
else
event->setAccepted(false);