: QTextDocument(parent), outstanding(0)
{
setUndoRedoEnabled(false);
+ documentLayout()->registerHandler(QTextFormat::ImageObject, this);
}
QQuickTextDocumentWithImageResources::~QQuickTextDocumentWithImageResources()
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(url, p);
-
- if (p->isLoading()) {
- p->connectFinished(this, SLOT(requestFinished()));
- outstanding++;
- }
- }
-
- QDeclarativePixmap *p = *iter;
- if (p->isReady()) {
- return p->image();
- } else if (p->isError()) {
- if (!errors.contains(url)) {
- errors.insert(url);
- qmlInfo(parent()) << p->error();
- }
- }
+ QDeclarativePixmap *p = loadPixmap(context, url);
+ return p->image();
}
return QTextDocument::loadResource(type,url); // The *resolved* URL
outstanding--;
if (outstanding == 0) {
markContentsDirty(0, characterCount());
-
- if (QQuickText *item = qobject_cast<QQuickText *>(parent()))
- QQuickTextPrivate::get(item)->updateLayout();
+ emit imagesLoaded();
}
}
QTextDocument::clear();
}
+
+QSizeF QQuickTextDocumentWithImageResources::intrinsicSize(
+ QTextDocument *, int, const QTextFormat &format)
+{
+ if (format.isImageFormat()) {
+ QTextImageFormat imageFormat = format.toImageFormat();
+
+ const bool hasWidth = imageFormat.hasProperty(QTextFormat::ImageWidth);
+ const int width = qRound(imageFormat.width());
+ const bool hasHeight = imageFormat.hasProperty(QTextFormat::ImageHeight);
+ const int height = qRound(imageFormat.height());
+
+ QSizeF size(width, height);
+ if (!hasWidth || !hasHeight) {
+ QDeclarativeContext *context = qmlContext(parent());
+ QUrl url = context->resolvedUrl(QUrl(imageFormat.name()));
+
+ QDeclarativePixmap *p = loadPixmap(context, url);
+ if (!p->isReady()) {
+ if (!hasWidth)
+ size.setWidth(16);
+ if (!hasHeight)
+ size.setHeight(16);
+ return size;
+ }
+ QSize implicitSize = p->implicitSize();
+
+ if (!hasWidth) {
+ if (!hasHeight)
+ size.setWidth(implicitSize.width());
+ else
+ size.setWidth(qRound(height * (implicitSize.width() / (qreal) implicitSize.height())));
+ }
+ if (!hasHeight) {
+ if (!hasWidth)
+ size.setHeight(implicitSize.height());
+ else
+ size.setHeight(qRound(width * (implicitSize.height() / (qreal) implicitSize.width())));
+ }
+ }
+ return size;
+ }
+ return QSizeF();
+}
+
+void QQuickTextDocumentWithImageResources::drawObject(
+ QPainter *, const QRectF &, QTextDocument *, int, const QTextFormat &)
+{
+}
+
+QImage QQuickTextDocumentWithImageResources::image(const QTextImageFormat &format)
+{
+ QDeclarativeContext *context = qmlContext(parent());
+ QUrl url = context->resolvedUrl(QUrl(format.name()));
+
+ QDeclarativePixmap *p = loadPixmap(context, url);
+ return p->image();
+}
+
+QDeclarativePixmap *QQuickTextDocumentWithImageResources::loadPixmap(
+ QDeclarativeContext *context, const QUrl &url)
+{
+
+ 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(url, p);
+
+ if (p->isLoading()) {
+ p->connectFinished(this, SLOT(requestFinished()));
+ outstanding++;
+ }
+ }
+
+ QDeclarativePixmap *p = *iter;
+ if (p->isError()) {
+ if (!errors.contains(url)) {
+ errors.insert(url);
+ qmlInfo(parent()) << p->error();
+ }
+ }
+ return p;
+}
+
void QQuickTextDocumentWithImageResources::clearResources()
{
foreach (QDeclarativePixmap *pixmap, m_resources)
return implicitWidth;
}
+void QQuickText::q_imagesLoaded()
+{
+ Q_D(QQuickText);
+ d->updateLayout();
+}
+
void QQuickTextPrivate::updateLayout()
{
Q_Q(QQuickText);
Q_Q(QQuickText);
doc = new QQuickTextDocumentWithImageResources(q);
doc->setDocumentMargin(0);
+ FAST_CONNECT(doc, SIGNAL(imagesLoaded()), q, SLOT(q_imagesLoaded()));
}
}
virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual bool event(QEvent *);
+private Q_SLOTS:
+ void q_imagesLoaded();
+
private:
Q_DISABLE_COPY(QQuickText)
Q_DECLARE_PRIVATE(QQuickText)
#include "qquickimplicitsizeitem_p_p.h"
#include <QtDeclarative/qdeclarative.h>
+#include <QtGui/qabstracttextdocumentlayout.h>
#include <QtGui/qtextlayout.h>
QT_BEGIN_NAMESPACE
};
class QDeclarativePixmap;
-class QQuickTextDocumentWithImageResources : public QTextDocument {
+class QQuickTextDocumentWithImageResources : public QTextDocument, public QTextObjectInterface
+{
Q_OBJECT
-
+ Q_INTERFACES(QTextObjectInterface)
public:
QQuickTextDocumentWithImageResources(QQuickItem *parent);
virtual ~QQuickTextDocumentWithImageResources();
void clear();
+ QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format);
+ void drawObject(QPainter *p, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format);
+
+ QImage image(const QTextImageFormat &format);
+
+Q_SIGNALS:
+ void imagesLoaded();
+
protected:
QVariant loadResource(int type, const QUrl &name);
+ QDeclarativePixmap *loadPixmap(QDeclarativeContext *context, const QUrl &name);
+
private slots:
void requestFinished();
#endif
FAST_CONNECT(document, SIGNAL(undoAvailable(bool)), q, SIGNAL(canUndoChanged()));
FAST_CONNECT(document, SIGNAL(redoAvailable(bool)), q, SIGNAL(canRedoChanged()));
+ FAST_CONNECT(document, SIGNAL(imagesLoaded()), q, SLOT(updateSize()));
document->setDefaultFont(font);
document->setDocumentMargin(textMargin);
qreal paintedWidth() const;
qreal paintedHeight() const;
+ QUrl baseUrl() const;
+ void setBaseUrl(const QUrl &url);
+ void resetBaseUrl();
+
Q_INVOKABLE QRectF positionToRectangle(int) const;
Q_INVOKABLE int positionAt(int x, int y) const;
Q_INVOKABLE void moveCursorSelection(int pos);
void updateDocument();
void updateCursor();
void q_updateAlignment();
+ void updateSize();
private:
- void updateSize();
void updateTotalLines();
void updateImageCache(const QRectF &rect = QRectF());
{
}
+ static QQuickTextEditPrivate *get(QQuickTextEdit *item) {
+ return static_cast<QQuickTextEditPrivate *>(QObjectPrivate::get(item)); }
+
void init();
void updateDefaultTextOption();
#include <qtexttable.h>
#include <qtextlist.h>
#include <private/qdeclarativestyledtext_p.h>
+#include <private/qquicktext_p_p.h>
#include <private/qfont_p.h>
#include <private/qfontengine_p.h>
#include <private/qrawfont_p.h>
if (format.objectType() == QTextFormat::ImageObject) {
QTextImageFormat imageFormat = format.toImageFormat();
- QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
- image = imageHandler->image(textDocument, imageFormat);
+ if (QQuickTextDocumentWithImageResources *imageDoc = qobject_cast<QQuickTextDocumentWithImageResources *>(textDocument)) {
+ image = imageDoc->image(imageFormat);
+
+ if (image.isNull())
+ return;
+ } else {
+ QTextImageHandler *imageHandler = static_cast<QTextImageHandler *>(handler);
+ image = imageHandler->image(textDocument, imageFormat);
+ }
}
if (image.isNull()) {
**
****************************************************************************/
#include <qtest.h>
+#include <QtTest/QSignalSpy>
#include <QTextDocument>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecomponent.h>
--- /dev/null
+import QtQuick 2.0
+
+TextEdit {
+ textFormat: TextEdit.RichText
+ text: "<img src='http/exists.png'>"
+}
--- /dev/null
+import QtQuick 2.0
+
+TextEdit {
+ textFormat: TextEdit.RichText
+ text: "<img src='http/notexists.png'>"
+}
--- /dev/null
+import QtQuick 2.0
+
+TextEdit {
+ textFormat: TextEdit.RichText
+ text: "<img src='http://127.0.0.1:42332/exists.png'>"
+}
--- /dev/null
+import QtQuick 2.0
+
+TextEdit {
+ textFormat: TextEdit.RichText
+ text: "<img src='http://127.0.0.1:42332/notexists.png'>"
+}
#include <QtGui/qguiapplication.h>
#include <private/qquicktextedit_p.h>
#include <private/qquicktextedit_p_p.h>
+#include <private/qquicktext_p_p.h>
#include <QFontMetrics>
#include <QtQuick/QQuickView>
#include <QDir>
void undo_keypressevents_data();
void undo_keypressevents();
+ void embeddedImages();
+ void embeddedImages_data();
+
void emptytags_QTBUG_22058();
private:
QVERIFY(textEdit->text().isEmpty());
}
+void tst_qquicktextedit::embeddedImages_data()
+{
+ QTest::addColumn<QUrl>("qmlfile");
+ QTest::addColumn<QString>("error");
+ QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
+ QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
+ << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
+ QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
+ QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
+ << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
+}
+
+void tst_qquicktextedit::embeddedImages()
+{
+ QFETCH(QUrl, qmlfile);
+ QFETCH(QString, error);
+
+ TestHTTPServer server(42332);
+ server.serveDirectory(testFile("http"));
+
+ if (!error.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
+
+ QDeclarativeComponent textComponent(&engine, qmlfile);
+ QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
+
+ QPixmap pm(testFile("http/exists.png"));
+ if (error.isEmpty()) {
+ QCOMPARE(textObject->width(), double(pm.width()));
+ QCOMPARE(textObject->height(), double(pm.height()));
+ } else {
+ QVERIFY(16 != pm.width()); // check test is effective
+ QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
+ QCOMPARE(textObject->height(), 16.0);
+ }
+
+ delete textObject;
+}
+
void tst_qquicktextedit::emptytags_QTBUG_22058()
{
QQuickView canvas(testFileUrl("qtbug-22058.qml"));