--- a/src/declarative/graphicsitems/qdeclarativetext.cpp Tue Jul 06 15:10:48 2010 +0300
+++ b/src/declarative/graphicsitems/qdeclarativetext.cpp Wed Aug 18 10:37:55 2010 +0300
@@ -57,56 +57,106 @@
QT_BEGIN_NAMESPACE
+extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
+
class QTextDocumentWithImageResources : public QTextDocument {
Q_OBJECT
public:
- QTextDocumentWithImageResources(QDeclarativeText *parent) :
- QTextDocument(parent),
- outstanding(0)
- {
- }
+ QTextDocumentWithImageResources(QDeclarativeText *parent);
+ virtual ~QTextDocumentWithImageResources();
+ void setText(const QString &);
int resourcesLoading() const { return outstanding; }
protected:
- QVariant loadResource(int type, const QUrl &name)
- {
- QUrl url = qmlContext(parent())->resolvedUrl(name);
+ QVariant loadResource(int type, const QUrl &name);
+
+private slots:
+ void requestFinished();
+
+private:
+ QHash<QUrl, QDeclarativePixmap *> m_resources;
+
+ int outstanding;
+ static QSet<QUrl> errors;
+};
+
+QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent)
+: QTextDocument(parent), outstanding(0)
+{
+}
- if (type == QTextDocument::ImageResource) {
- QPixmap pm;
- QString errorString;
- QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(url, &pm, &errorString, 0, false, 0, 0);
- if (status == QDeclarativePixmapReply::Ready)
- return pm;
- if (status == QDeclarativePixmapReply::Error) {
- if (!errors.contains(url)) {
- errors.insert(url);
- qmlInfo(parent()) << errorString;
- }
- } else {
- QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(parent()), url);
- connect(reply, SIGNAL(finished()), this, SLOT(requestFinished()));
+QTextDocumentWithImageResources::~QTextDocumentWithImageResources()
+{
+ if (!m_resources.isEmpty())
+ qDeleteAll(m_resources);
+}
+
+QVariant QTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
+{
+ QDeclarativeContext *context = qmlContext(parent());
+ QUrl url = context->resolvedUrl(name);
+
+ if (type == QTextDocument::ImageResource) {
+ QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
+
+ if (iter == m_resources.end()) {
+ QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
+ iter = m_resources.insert(name, p);
+
+ if (p->isLoading()) {
+ p->connectFinished(this, SLOT(requestFinished()));
outstanding++;
}
}
- return QTextDocument::loadResource(type,url); // The *resolved* URL
+ QDeclarativePixmap *p = *iter;
+ if (p->isReady()) {
+ return p->pixmap();
+ } else if (p->isError()) {
+ if (!errors.contains(url)) {
+ errors.insert(url);
+ qmlInfo(parent()) << p->error();
+ }
+ }
}
-private slots:
- void requestFinished()
- {
- outstanding--;
- if (outstanding == 0)
- static_cast<QDeclarativeText*>(parent())->reloadWithResources();
+ return QTextDocument::loadResource(type,url); // The *resolved* URL
+}
+
+void QTextDocumentWithImageResources::requestFinished()
+{
+ outstanding--;
+ if (outstanding == 0) {
+ QDeclarativeText *textItem = static_cast<QDeclarativeText*>(parent());
+ QString text = textItem->text();
+#ifndef QT_NO_TEXTHTMLPARSER
+ setHtml(text);
+#else
+ setPlainText(text);
+#endif
+ QDeclarativeTextPrivate *d = QDeclarativeTextPrivate::get(textItem);
+ d->updateLayout();
+ d->markImgDirty();
+ }
+}
+
+void QTextDocumentWithImageResources::setText(const QString &text)
+{
+ if (!m_resources.isEmpty()) {
+ qWarning("CLEAR");
+ qDeleteAll(m_resources);
+ m_resources.clear();
+ outstanding = 0;
}
-private:
- int outstanding;
- static QSet<QUrl> errors;
-};
+#ifndef QT_NO_TEXTHTMLPARSER
+ setHtml(text);
+#else
+ setPlainText(text);
+#endif
+}
QSet<QUrl> QTextDocumentWithImageResources::errors;
@@ -126,16 +176,18 @@
\image declarative-text.png
If height and width are not explicitly set, Text will attempt to determine how
- much room is needed and set it accordingly. Unless \c wrapMode is set, it will always
+ much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
prefer width to height (all text will be placed on a single line).
- The \c elide property can alternatively be used to fit a single line of
+ The \l elide property can alternatively be used to fit a single line of
plain text to a set width.
Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
HTML img tags that load remote images, the text is reloaded.
Text provides read-only text. For editable text, see \l TextEdit.
+
+ \sa {declarative/text/fonts}{Fonts example}
*/
/*!
@@ -161,7 +213,7 @@
The \c elide property can alternatively be used to fit a line of plain text to a set width.
- A QDeclarativeText object can be instantiated in Qml using the tag \c Text.
+ A QDeclarativeText object can be instantiated in QML using the tag \c Text.
*/
QDeclarativeText::QDeclarativeText(QDeclarativeItem *parent)
: QDeclarativeItem(*(new QDeclarativeTextPrivate), parent)
@@ -225,12 +277,6 @@
*/
/*!
- \qmlproperty bool Text::font.outline
-
- Sets whether the font has an outline style.
-*/
-
-/*!
\qmlproperty bool Text::font.strikeout
Sets whether the font has a strikeout style.
@@ -257,8 +303,7 @@
Sets the letter spacing for the font.
Letter spacing changes the default spacing between individual letters in the font.
- A value of 100 will keep the spacing unchanged; a value of 200 will enlarge the spacing after a character by
- the width of the character itself.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
*/
/*!
@@ -318,7 +363,7 @@
if (d->richText) {
if (isComponentComplete()) {
d->ensureDoc();
- d->doc->setHtml(n);
+ d->doc->setText(n);
}
}
@@ -437,6 +482,8 @@
\qml
Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
\endqml
+
+ \sa style
*/
QColor QDeclarativeText::styleColor() const
{
@@ -473,6 +520,7 @@
return;
d->hAlign = align;
+ update();
emit horizontalAlignmentChanged(align);
}
@@ -489,6 +537,7 @@
return;
d->vAlign = align;
+ update();
emit verticalAlignmentChanged(align);
}
@@ -499,16 +548,11 @@
wrap if an explicit width has been set. wrapMode can be one of:
\list
- \o Text.NoWrap - no wrapping will be performed.
- \o Text.WordWrap - wrapping is done on word boundaries. If the text cannot be
- word-wrapped to the specified width it will be partially drawn outside of the item's bounds.
- If this is undesirable then enable clipping on the item (Item::clip).
- \o Text.WrapAnywhere - Text can be wrapped at any point on a line, even if it occurs in the middle of a word.
- \o Text.WrapAtWordBoundaryOrAnywhere - 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.
+ \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.
\endlist
-
- The default is Text.NoWrap.
*/
QDeclarativeText::WrapMode QDeclarativeText::wrapMode() const
{
@@ -538,13 +582,13 @@
Supported text formats are:
\list
- \o Text.AutoText
+ \o Text.AutoText (default)
\o Text.PlainText
\o Text.RichText
\o Text.StyledText
\endlist
- The default is Text.AutoText. If the text format is Text.AutoText the text element
+ If the text format is \c Text.AutoText the text element
will automatically determine whether the text should be treated as
rich text. This determination is made using Qt::mightBeRichText().
@@ -608,7 +652,7 @@
} else if (!wasRich && d->richText) {
if (isComponentComplete()) {
d->ensureDoc();
- d->doc->setHtml(d->text);
+ d->doc->setText(d->text);
}
d->updateLayout();
d->markImgDirty();
@@ -658,11 +702,54 @@
emit elideModeChanged(d->elideMode);
}
+QRectF QDeclarativeText::boundingRect() const
+{
+ Q_D(const QDeclarativeText);
+
+ int w = width();
+ int h = height();
+
+ int x = 0;
+ int y = 0;
+
+ QSize size = d->cachedLayoutSize;
+ if (d->style != Normal)
+ size += QSize(2,2);
+
+ // Could include font max left/right bearings to either side of rectangle.
+
+ switch (d->hAlign) {
+ case AlignLeft:
+ x = 0;
+ break;
+ case AlignRight:
+ x = w - size.width();
+ break;
+ case AlignHCenter:
+ x = (w - size.width()) / 2;
+ break;
+ }
+
+ switch (d->vAlign) {
+ case AlignTop:
+ y = 0;
+ break;
+ case AlignBottom:
+ y = h - size.height();
+ break;
+ case AlignVCenter:
+ y = (h - size.height()) / 2;
+ break;
+ }
+
+ return QRectF(x,y,size.width(),size.height());
+}
+
void QDeclarativeText::geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry)
{
Q_D(QDeclarativeText);
- if (newGeometry.width() != oldGeometry.width()) {
+ if (!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) {
if (d->wrapMode != QDeclarativeText::NoWrap || d->elideMode != QDeclarativeText::ElideNone) {
//re-elide if needed
if (d->singleline && d->elideMode != QDeclarativeText::ElideNone &&
@@ -708,6 +795,7 @@
}
}
+
void QDeclarativeTextPrivate::updateSize()
{
Q_Q(QDeclarativeText);
@@ -715,6 +803,7 @@
QFontMetrics fm(font);
if (text.isEmpty()) {
q->setImplicitHeight(fm.height());
+ emit q->paintedSizeChanged();
return;
}
@@ -724,7 +813,10 @@
//setup instance of QTextLayout for all cases other than richtext
if (!richText) {
size = setupTextLayout(&layout);
- cachedLayoutSize = size;
+ if (cachedLayoutSize != size) {
+ q->prepareGeometryChange();
+ cachedLayoutSize = size;
+ }
dy -= size.height();
} else {
singleline = false; // richtext can't elide or be optimized for single-line case
@@ -738,7 +830,13 @@
else
doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
dy -= (int)doc->size().height();
- cachedLayoutSize = doc->size().toSize();
+ q->prepareGeometryChange();
+ QSize dsize = doc->size().toSize();
+ if (dsize != cachedLayoutSize) {
+ q->prepareGeometryChange();
+ cachedLayoutSize = dsize;
+ }
+ size = QSize(int(doc->idealWidth()),dsize.height());
}
int yoff = 0;
@@ -751,13 +849,40 @@
q->setBaselineOffset(fm.ascent() + yoff);
//### need to comfirm cost of always setting these for richText
- q->setImplicitWidth(richText ? (int)doc->idealWidth() : size.width());
- q->setImplicitHeight(richText ? (int)doc->size().height() : size.height());
+ internalWidthUpdate = true;
+ q->setImplicitWidth(size.width());
+ internalWidthUpdate = false;
+ q->setImplicitHeight(size.height());
+ emit q->paintedSizeChanged();
} else {
dirty = true;
}
}
+/*!
+ \qmlproperty real Text::paintedWidth
+
+ Returns the width of the text, including width past the width
+ which is covered due to insufficient wrapping if WrapMode is set.
+*/
+qreal QDeclarativeText::paintedWidth() const
+{
+ return implicitWidth();
+}
+
+/*!
+ \qmlproperty real Text::paintedHeight
+
+ 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 QDeclarativeText::paintedHeight() const
+{
+ return implicitHeight();
+}
+
+
+
// ### text layout handling should be profiled and optimized as needed
// what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
@@ -782,6 +907,8 @@
ppm.drawPixmap(pos, imgCache);
ppm.end();
+ if (imgCache.size() != img.size())
+ q_func()->prepareGeometryChange();
imgCache = img;
}
@@ -800,6 +927,8 @@
ppm.drawPixmap(pos, imgCache);
ppm.end();
+ if (imgCache.size() != img.size())
+ q_func()->prepareGeometryChange();
imgCache = img;
}
@@ -876,7 +1005,14 @@
QPixmap img(size);
if (!size.isEmpty()) {
img.fill(Qt::transparent);
+#ifdef Q_WS_MAC
+ bool oldSmooth = qt_applefontsmoothing_enabled;
+ qt_applefontsmoothing_enabled = false;
+#endif
QPainter p(&img);
+#ifdef Q_WS_MAC
+ qt_applefontsmoothing_enabled = oldSmooth;
+#endif
drawWrappedText(&p, QPointF(0,0), drawStyle);
}
return img;
@@ -899,7 +1035,14 @@
//paint text
QPixmap img(size);
img.fill(Qt::transparent);
+#ifdef Q_WS_MAC
+ bool oldSmooth = qt_applefontsmoothing_enabled;
+ qt_applefontsmoothing_enabled = false;
+#endif
QPainter p(&img);
+#ifdef Q_WS_MAC
+ qt_applefontsmoothing_enabled = oldSmooth;
+#endif
QAbstractTextDocumentLayout::PaintContext context;
@@ -924,18 +1067,21 @@
return;
bool empty = text.isEmpty();
+ QPixmap newImgCache;
if (empty) {
- imgCache = QPixmap();
imgStyleCache = QPixmap();
} else if (richText) {
- imgCache = richTextImage(false);
+ newImgCache = richTextImage(false);
if (style != QDeclarativeText::Normal)
imgStyleCache = richTextImage(true); //### should use styleColor
} else {
- imgCache = wrappedTextImage(false);
+ newImgCache = wrappedTextImage(false);
if (style != QDeclarativeText::Normal)
imgStyleCache = wrappedTextImage(true); //### should use styleColor
}
+ if (imgCache.size() != newImgCache.size())
+ q_func()->prepareGeometryChange();
+ imgCache = newImgCache;
if (!empty)
switch (style) {
case QDeclarativeText::Outline:
@@ -963,16 +1109,6 @@
}
}
-void QDeclarativeText::reloadWithResources()
-{
- Q_D(QDeclarativeText);
- if (!d->richText)
- return;
- d->doc->setHtml(d->text);
- d->updateLayout();
- d->markImgDirty();
-}
-
/*!
Returns the number of resources (images) that are being loaded asynchronously.
*/
@@ -982,6 +1118,15 @@
return d->doc ? d->doc->resourcesLoading() : 0;
}
+/*!
+ \qmlproperty bool Text::clip
+ This property holds whether the text is clipped.
+
+ Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
+
+ If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
+*/
+
void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
{
Q_D(QDeclarativeText);
@@ -996,72 +1141,29 @@
if (d->smooth)
p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth);
- int w = width();
- int h = height();
-
- int x = 0;
- int y = 0;
-
- switch (d->hAlign) {
- case AlignLeft:
- x = 0;
- break;
- case AlignRight:
- x = w - d->imgCache.width();
- break;
- case AlignHCenter:
- x = (w - d->imgCache.width()) / 2;
- break;
- }
-
- switch (d->vAlign) {
- case AlignTop:
- y = 0;
- break;
- case AlignBottom:
- y = h - d->imgCache.height();
- break;
- case AlignVCenter:
- y = (h - d->imgCache.height()) / 2;
- break;
- }
+ QRect br = boundingRect().toRect();
bool needClip = clip() && (d->imgCache.width() > width() ||
d->imgCache.height() > height());
- if (needClip) {
- p->save();
- p->setClipRect(boundingRect(), Qt::IntersectClip);
- }
- p->drawPixmap(x, y, d->imgCache);
if (needClip)
- p->restore();
+ p->drawPixmap(0, 0, width(), height(), d->imgCache, -br.x(), -br.y(), width(), height());
+ else
+ p->drawPixmap(br.x(), br.y(), d->imgCache);
if (d->smooth) {
p->setRenderHint(QPainter::Antialiasing, oldAA);
p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
}
} else {
- int h = height();
- int y = 0;
+ qreal y = boundingRect().y();
- switch (d->vAlign) {
- case AlignTop:
- y = 0;
- break;
- case AlignBottom:
- y = h - d->cachedLayoutSize.height();
- break;
- case AlignVCenter:
- y = (h - d->cachedLayoutSize.height()) / 2;
- break;
- }
bool needClip = !clip() && (d->cachedLayoutSize.width() > width() ||
d->cachedLayoutSize.height() > height());
if (needClip) {
p->save();
- p->setClipRect(boundingRect(), Qt::IntersectClip);
+ p->setClipRect(0, 0, width(), height(), Qt::IntersectClip);
}
if (d->richText) {
QAbstractTextDocumentLayout::PaintContext context;
@@ -1098,7 +1200,7 @@
if (d->dirty) {
if (d->richText) {
d->ensureDoc();
- d->doc->setHtml(d->text);
+ d->doc->setText(d->text);
}
d->updateLayout();
d->dirty = false;
@@ -1128,7 +1230,7 @@
}
/*!
- \qmlsignal Text::linkActivated(link)
+ \qmlsignal Text::onLinkActivated(link)
This handler is called when the user clicks on a link embedded in the text.
*/