From aad82abd84022f5401c6b79ab15d24abebf082af Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Thu, 15 Apr 2010 14:47:10 +1000 Subject: Correctly resolve, and load, IMG tags in Text element. Task-number: QTBUG-9900 Reviewed-by: Martin Jones --- src/declarative/graphicsitems/qdeclarativetext.cpp | 102 +++++++++++++++++++-- src/declarative/graphicsitems/qdeclarativetext_p.h | 7 ++ .../graphicsitems/qdeclarativetext_p_p.h | 5 +- .../qdeclarativetext/data/embeddedImagesLocal.qml | 5 + .../data/embeddedImagesLocalError.qml | 5 + .../qdeclarativetext/data/embeddedImagesRemote.qml | 5 + .../data/embeddedImagesRemoteError.qml | 5 + .../qdeclarativetext/data/http/exists.png | Bin 0 -> 2738 bytes .../qdeclarativetext/qdeclarativetext.pro | 7 ++ .../qdeclarativetext/tst_qdeclarativetext.cpp | 45 +++++++++ 10 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocal.qml create mode 100644 tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocalError.qml create mode 100644 tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemote.qml create mode 100644 tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemoteError.qml create mode 100644 tests/auto/declarative/qdeclarativetext/data/http/exists.png diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 3a35dd5..3b39fa4 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -43,6 +43,7 @@ #include "private/qdeclarativetext_p_p.h" #include #include +#include #include #include @@ -55,6 +56,50 @@ QT_BEGIN_NAMESPACE +class QTextDocumentWithImageResources : public QTextDocument { + Q_OBJECT + +public: + QTextDocumentWithImageResources(QDeclarativeText *parent) : + QTextDocument(parent), + outstanding(0) + { + } + + int resourcesLoading() const { return outstanding; } + +protected: + QVariant loadResource(int type, const QUrl &name) + { + QUrl url = qmlContext(parent())->resolvedUrl(name); + + if (type == QTextDocument::ImageResource) { + QPixmap pm; + QDeclarativePixmapReply::Status status = QDeclarativePixmapCache::get(url, &pm, 0, true, 0, 0); + if (status == QDeclarativePixmapReply::Ready) + return pm; + if (status != QDeclarativePixmapReply::Error) { + QDeclarativePixmapReply *reply = QDeclarativePixmapCache::request(qmlEngine(parent()), url); + connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + outstanding++; + static_cast(parent())->reloadWithResources(); + } + } + + return QTextDocument::loadResource(type,url); // The *resolved* URL + } + +private slots: + void requestFinished() + { + outstanding--; + static_cast(parent())->reloadWithResources(); + } + +private: + int outstanding; +}; + /*! \qmlclass Text QDeclarativeText \since 4.7 @@ -77,6 +122,9 @@ QT_BEGIN_NAMESPACE The \c 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, and that if IMG tags + load remote images, the text reloads (see resourcesLoading). + Text provides read-only text. For editable text, see \l TextEdit. */ @@ -259,11 +307,10 @@ void QDeclarativeText::setText(const QString &n) d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n)); if (d->richText) { - if (!d->doc) { - d->doc = new QTextDocument(this); - d->doc->setDocumentMargin(0); + if (isComponentComplete()) { + d->ensureDoc(); + d->doc->setHtml(n); } - d->doc->setHtml(n); } d->text = n; @@ -550,11 +597,10 @@ void QDeclarativeText::setTextFormat(TextFormat format) d->updateLayout(); d->markImgDirty(); } else if (!wasRich && d->richText) { - if (!d->doc) { - d->doc = new QTextDocument(this); - d->doc->setDocumentMargin(0); + if (isComponentComplete()) { + d->ensureDoc(); + d->doc->setHtml(d->text); } - d->doc->setHtml(d->text); d->updateLayout(); d->markImgDirty(); } @@ -898,6 +944,39 @@ void QDeclarativeTextPrivate::checkImgCache() imgDirty = false; } +void QDeclarativeTextPrivate::ensureDoc() +{ + if (!doc) { + Q_Q(QDeclarativeText); + doc = new QTextDocumentWithImageResources(q); + doc->setDocumentMargin(0); + } +} + +void QDeclarativeText::reloadWithResources() +{ + Q_D(QDeclarativeText); + if (!d->richText) + return; + if (resourcesLoading()!=0) + return; + emit resourcesLoadingChanged(); + d->doc->setHtml(d->text); + d->updateLayout(); + d->markImgDirty(); +} + +/*! + \qmlproperty int Text::resourcesLoading + This property is the number of resources (images) that are being loaded asynchronously. +*/ +int QDeclarativeText::resourcesLoading() const +{ + Q_D(const QDeclarativeText); + return d->doc ? d->doc->resourcesLoading() : 0; +} + + void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) { Q_D(QDeclarativeText); @@ -1011,6 +1090,10 @@ void QDeclarativeText::componentComplete() Q_D(QDeclarativeText); QDeclarativeItem::componentComplete(); if (d->dirty) { + if (d->richText) { + d->ensureDoc(); + d->doc->setHtml(d->text); + } d->updateLayout(); d->dirty = false; } @@ -1061,4 +1144,7 @@ void QDeclarativeText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (!event->isAccepted()) QDeclarativeItem::mouseReleaseEvent(event); } + QT_END_NAMESPACE + +#include "qdeclarativetext.moc" diff --git a/src/declarative/graphicsitems/qdeclarativetext_p.h b/src/declarative/graphicsitems/qdeclarativetext_p.h index 871c833..fcad898 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p.h @@ -72,6 +72,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeText : public QDeclarativeItem Q_PROPERTY(bool wrap READ wrap WRITE setWrap NOTIFY wrapModeChanged) Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged) Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode? + Q_PROPERTY(int resourcesLoading READ resourcesLoading NOTIFY resourcesLoadingChanged) public: QDeclarativeText(QDeclarativeItem *parent=0); @@ -138,6 +139,8 @@ public: virtual void componentComplete(); + int resourcesLoading() const; + Q_SIGNALS: void textChanged(const QString &text); void linkActivated(const QString &link); @@ -150,6 +153,7 @@ Q_SIGNALS: void wrapModeChanged(); void textFormatChanged(TextFormat textFormat); void elideModeChanged(TextElideMode mode); + void resourcesLoadingChanged(); protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); @@ -160,6 +164,9 @@ protected: private: Q_DISABLE_COPY(QDeclarativeText) Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeText) + + friend class QTextDocumentWithImageResources; + void reloadWithResources(); }; QT_END_NAMESPACE diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h index cc5a9f2..332f99e 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE class QTextLayout; -class QTextDocument; +class QTextDocumentWithImageResources; class QDeclarativeTextPrivate : public QDeclarativeItemPrivate { @@ -84,6 +84,7 @@ public: ~QDeclarativeTextPrivate(); + void ensureDoc(); void updateSize(); void updateLayout(); void markImgDirty() { @@ -118,7 +119,7 @@ public: bool richText:1; bool singleline:1; bool cache:1; - QTextDocument *doc; + QTextDocumentWithImageResources *doc; QTextLayout layout; QSize cachedLayoutSize; QDeclarativeText::TextFormat format; diff --git a/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocal.qml b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocal.qml new file mode 100644 index 0000000..5aeea56 --- /dev/null +++ b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocal.qml @@ -0,0 +1,5 @@ +import Qt 4.6 + +Text { + text: "" +} diff --git a/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocalError.qml b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocalError.qml new file mode 100644 index 0000000..17bb21c --- /dev/null +++ b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesLocalError.qml @@ -0,0 +1,5 @@ +import Qt 4.6 + +Text { + text: "" +} diff --git a/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemote.qml b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemote.qml new file mode 100644 index 0000000..53b0266 --- /dev/null +++ b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemote.qml @@ -0,0 +1,5 @@ +import Qt 4.6 + +Text { + text: "" +} diff --git a/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemoteError.qml b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemoteError.qml new file mode 100644 index 0000000..48c7a95 --- /dev/null +++ b/tests/auto/declarative/qdeclarativetext/data/embeddedImagesRemoteError.qml @@ -0,0 +1,5 @@ +import Qt 4.6 + +Text { + text: "" +} diff --git a/tests/auto/declarative/qdeclarativetext/data/http/exists.png b/tests/auto/declarative/qdeclarativetext/data/http/exists.png new file mode 100644 index 0000000..399bd0b Binary files /dev/null and b/tests/auto/declarative/qdeclarativetext/data/http/exists.png differ diff --git a/tests/auto/declarative/qdeclarativetext/qdeclarativetext.pro b/tests/auto/declarative/qdeclarativetext/qdeclarativetext.pro index 73c05b5..e70443e 100644 --- a/tests/auto/declarative/qdeclarativetext/qdeclarativetext.pro +++ b/tests/auto/declarative/qdeclarativetext/qdeclarativetext.pro @@ -1,8 +1,15 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative gui +QT += network macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativetext.cpp +INCLUDEPATH += ../shared/ +HEADERS += ../shared/testhttpserver.h +SOURCES += ../shared/testhttpserver.cpp + +DEFINES += SRCDIR=\\\"$$PWD\\\" + CONFIG += parallel_test diff --git a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp index 6637415..53640d0 100644 --- a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp +++ b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp @@ -48,6 +48,9 @@ #include #include +#include "../../../shared/util.h" +#include "testhttpserver.h" + class tst_qdeclarativetext : public QObject { @@ -62,6 +65,9 @@ private slots: void elide(); void textFormat(); + void embeddedImages_data(); + void embeddedImages(); + // ### these tests may be trivial void horizontalAlignment(); void verticalAlignment(); @@ -859,6 +865,45 @@ void tst_qdeclarativetext::clickLink() } } +void tst_qdeclarativetext::embeddedImages_data() +{ + QTest::addColumn("qmlfile"); + QTest::addColumn("error"); + QTest::newRow("local") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocal.qml") << ""; + QTest::newRow("local-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesLocalError.qml") + << "\"Cannot open: " + QUrl::fromLocalFile(SRCDIR "/data/http/notexists.png").toString() + "\" "; + QTest::newRow("remote") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemote.qml") << ""; + QTest::newRow("remote-error") << QUrl::fromLocalFile(SRCDIR "/data/embeddedImagesRemoteError.qml") + << "\"Error downloading http://127.0.0.1:14453/notexists.png - server replied: Not found\" "; +} + +void tst_qdeclarativetext::embeddedImages() +{ + // Tests QTBUG-9900 + + QFETCH(QUrl, qmlfile); + QFETCH(QString, error); + + TestHTTPServer server(14453); + server.serveDirectory(SRCDIR "/data/http"); + + if (!error.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, error.toLatin1()); + + QDeclarativeComponent textComponent(&engine, qmlfile); + QDeclarativeText *textObject = qobject_cast(textComponent.create()); + + QVERIFY(textObject != 0); + + QTRY_COMPARE(textObject->resourcesLoading(), 0); + + if (error.isEmpty()) { + QPixmap pm(SRCDIR "/data/http/exists.png"); + QCOMPARE(textObject->width(), double(pm.width())); + QCOMPARE(textObject->height(), double(pm.height())); + } +} + QTEST_MAIN(tst_qdeclarativetext) #include "tst_qdeclarativetext.moc" -- cgit v0.12