From 376e3bd8ecf40881685714f6f19e12d68e92127e Mon Sep 17 00:00:00 2001 From: Alexander Volkov Date: Tue, 25 Sep 2018 18:09:28 +0300 Subject: Introduce QUrlResourceProvider to load resources for HTML QTextDocument and the text editor classes suggest to override their loadResource() methods to provide data associated with a text document. This approach has the following drawbacks: - it requires subclassing - there is no way to set a global resource provider - QLabel is missing virtual loadResource() method and it can't be added without breaking ABI QUrlResourceProvider is designed to solve these issues. One should create a derived class that implements QUrlResourceProvider::resource(). The objects of the derived class then can be set for any text document. The default resource provider can be set with QUrlResourceProvider::setDefaultProvider(). This change also adds QLabel::setResourceProvider(), which doesn't break ABI. [ChangeLog][QtGui][Text] Introduced QUrlResourceProvider that allows to load resources for HTML. It is intended to replace the use of QTextDocument::loadResource(). Change-Id: Iaf19b229f522a73508f20715257450fe58f68daf Reviewed-by: Konstantin Ritt Reviewed-by: Lars Knoll --- src/gui/CMakeLists.txt | 1 + src/gui/text/qtextdocument.cpp | 35 ++++++++- src/gui/text/qtextdocument.h | 4 + src/gui/text/qtextdocument_p.cpp | 4 +- src/gui/text/qtextdocument_p.h | 1 + src/gui/text/qurlresourceprovider.cpp | 88 ++++++++++++++++++++++ src/gui/text/qurlresourceprovider.h | 61 +++++++++++++++ src/widgets/widgets/qlabel.cpp | 31 +++++++- src/widgets/widgets/qlabel.h | 4 + src/widgets/widgets/qlabel_p.h | 1 + src/widgets/widgets/qwidgettextcontrol.cpp | 1 + .../gui/text/qtextdocument/tst_qtextdocument.cpp | 25 ++++++ tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp | 26 +++++++ 13 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 src/gui/text/qurlresourceprovider.cpp create mode 100644 src/gui/text/qurlresourceprovider.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 802c0a2ecd..30e28cfd0e 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -242,6 +242,7 @@ qt_internal_add_module(Gui text/qtextobject.cpp text/qtextobject.h text/qtextobject_p.h text/qtextoption.cpp text/qtextoption.h text/qtexttable.cpp text/qtexttable.h text/qtexttable_p.h + text/qurlresourceprovider.cpp text/qurlresourceprovider.h util/qabstractlayoutstyleinfo.cpp util/qabstractlayoutstyleinfo_p.h util/qastchandler.cpp util/qastchandler_p.h util/qdesktopservices.cpp util/qdesktopservices.h diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 0fae775bae..2e9293eb99 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -46,6 +46,7 @@ #include "qtextdocumentfragment_p.h" #include "qtexttable.h" #include "qtextlist.h" +#include "qurlresourceprovider.h" #include #if QT_CONFIG(regularexpression) #include @@ -354,6 +355,7 @@ QTextDocument *QTextDocument::clone(QObject *parent) const priv->setDefaultFont(d->defaultFont()); priv->resources = d->resources; priv->cachedResources.clear(); + priv->resourceProvider = d->resourceProvider; #ifndef QT_NO_CSSPARSER priv->defaultStyleSheet = d->defaultStyleSheet; priv->parsedDefaultStyleSheet = d->parsedDefaultStyleSheet; @@ -2087,8 +2089,15 @@ QVariant QTextDocument::resource(int type, const QUrl &name) const QVariant r = d->resources.value(url); if (!r.isValid()) { r = d->cachedResources.value(url); - if (!r.isValid()) + if (!r.isValid()) { r = const_cast(this)->loadResource(type, url); + if (!r.isValid()) { + if (d->resourceProvider) + r = d->resourceProvider->resource(url); + else if (auto defaultProvider = QUrlResourceProvider::defaultProvider()) + r = defaultProvider->resource(url); + } + } } return r; } @@ -2118,6 +2127,30 @@ void QTextDocument::addResource(int type, const QUrl &name, const QVariant &reso d->resources.insert(name, resource); } +/*! + \since 6.1 + + Returns the resource provider for this text document. +*/ +QUrlResourceProvider *QTextDocument::resourceProvider() const +{ + Q_D(const QTextDocument); + return d->resourceProvider; +} + +/*! + \since 6.1 + + Sets the \a provider of resources for the text document. + + \note The text document \e{does not} take ownership of the \a provider. +*/ +void QTextDocument::setResourceProvider(QUrlResourceProvider *provider) +{ + Q_D(QTextDocument); + d->resourceProvider = provider; +} + /*! Loads data of the specified \a type from the resource with the given \a name. diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index 23fffc4771..ea57b697a8 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -68,6 +68,7 @@ class QVariant; class QRectF; class QTextOption; class QTextCursor; +class QUrlResourceProvider; namespace Qt @@ -239,6 +240,9 @@ public: QVariant resource(int type, const QUrl &name) const; void addResource(int type, const QUrl &name, const QVariant &resource); + QUrlResourceProvider *resourceProvider() const; + void setResourceProvider(QUrlResourceProvider *provider); + QList allFormats() const; void markContentsDirty(int from, int length); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index d6da0f38d2..1f2e8d2da2 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -52,6 +52,7 @@ #include "qtextdocumentlayout_p.h" #include "qtexttable.h" #include "qtextengine_p.h" +#include "qurlresourceprovider.h" #include @@ -186,7 +187,8 @@ QTextDocumentPrivate::QTextDocumentPrivate() docChangeLength(0), framesDirty(true), rtFrame(nullptr), - initialBlockCharFormatIndex(-1) // set correctly later in init() + initialBlockCharFormatIndex(-1), // set correctly later in init() + resourceProvider(nullptr) { editBlock = 0; editBlockCursorPosition = -1; diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index b4e0f526d0..d04113b320 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -366,6 +366,7 @@ private: QMap objects; QMap resources; QMap cachedResources; + QUrlResourceProvider *resourceProvider; QString defaultStyleSheet; int lastBlockCount; diff --git a/src/gui/text/qurlresourceprovider.cpp b/src/gui/text/qurlresourceprovider.cpp new file mode 100644 index 0000000000..6a50504471 --- /dev/null +++ b/src/gui/text/qurlresourceprovider.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Alexander Volkov +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qurlresourceprovider.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QUrlResourceProvider + \inmodule QtGui + \since 6.1 + \brief The QUrlResourceProvider is the base class of resource providers for QTextDocument. + \note An implementation should be thread-safe if it can be accessed from different threads, + e.g. when the default resource provider lives in the main thread and a QTexDocument lives + outside the main thread. +*/ + +static QAtomicPointer qt_provider; + +/*! + Destroys the resource provider. +*/ +QUrlResourceProvider::~QUrlResourceProvider() +{ + qt_provider.testAndSetRelease(this, nullptr); +} + +/*! + \fn virtual QVariant QUrlResourceProvider::resource(const QUrl &url) = 0; + + Returns data specified by the \a url. +*/ + +/*! + Returns the default resource provider. +*/ +QUrlResourceProvider *QUrlResourceProvider::defaultProvider() +{ + return qt_provider.loadAcquire(); +} + +/*! + Set the default resource provider to \a provider. +*/ +void QUrlResourceProvider::setDefaultProvider(QUrlResourceProvider *provider) +{ + qt_provider.storeRelease(provider); +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qurlresourceprovider.h b/src/gui/text/qurlresourceprovider.h new file mode 100644 index 0000000000..587a371a83 --- /dev/null +++ b/src/gui/text/qurlresourceprovider.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Alexander Volkov +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QURLRESOURCEPROVIDER_H +#define QURLRESOURCEPROVIDER_H + +#include +#include + +QT_BEGIN_NAMESPACE + + +class Q_GUI_EXPORT QUrlResourceProvider +{ +public: + virtual ~QUrlResourceProvider(); + virtual QVariant resource(const QUrl &url) = 0; + + static QUrlResourceProvider *defaultProvider(); + static void setDefaultProvider(QUrlResourceProvider *provider); +}; + +QT_END_NAMESPACE + +#endif // QURLRESOURCEPROVIDER_H diff --git a/src/widgets/widgets/qlabel.cpp b/src/widgets/widgets/qlabel.cpp index e124022e52..2d7b7f79b1 100644 --- a/src/widgets/widgets/qlabel.cpp +++ b/src/widgets/widgets/qlabel.cpp @@ -100,7 +100,8 @@ QLabelPrivate::QLabelPrivate() validCursor(false), onAnchor(false), #endif - openExternalLinks(false) + openExternalLinks(false), + resourceProvider(nullptr) { } @@ -1423,6 +1424,32 @@ void QLabel::setTextFormat(Qt::TextFormat format) } } +/*! + \since 6.1 + + Returns the resource provider for rich text of this label. +*/ +QUrlResourceProvider *QLabel::resourceProvider() const +{ + Q_D(const QLabel); + return d->control ? d->control->document()->resourceProvider() : d->resourceProvider; +} + +/*! + \since 6.1 + + Sets the \a provider of resources for rich text of this label. + + \note The label \e{does not} take ownership of the \a provider. +*/ +void QLabel::setResourceProvider(QUrlResourceProvider *provider) +{ + Q_D(QLabel); + d->resourceProvider = provider; + if (d->control != nullptr) + d->control->document()->setResourceProvider(provider); +} + /*! \reimp */ @@ -1589,6 +1616,8 @@ void QLabelPrivate::ensureTextControl() const control = new QWidgetTextControl(const_cast(q)); control->document()->setUndoRedoEnabled(false); control->document()->setDefaultFont(q->font()); + if (resourceProvider != nullptr) + control->document()->setResourceProvider(resourceProvider); control->setTextInteractionFlags(textInteractionFlags); control->setOpenExternalLinks(openExternalLinks); control->setPalette(q->palette()); diff --git a/src/widgets/widgets/qlabel.h b/src/widgets/widgets/qlabel.h index 8cc86b421e..69aea6d57a 100644 --- a/src/widgets/widgets/qlabel.h +++ b/src/widgets/widgets/qlabel.h @@ -49,6 +49,7 @@ QT_REQUIRE_CONFIG(label); QT_BEGIN_NAMESPACE +class QUrlResourceProvider; class QLabelPrivate; class Q_WIDGETS_EXPORT QLabel : public QFrame @@ -92,6 +93,9 @@ public: Qt::TextFormat textFormat() const; void setTextFormat(Qt::TextFormat); + QUrlResourceProvider *resourceProvider() const; + void setResourceProvider(QUrlResourceProvider *provider); + Qt::Alignment alignment() const; void setAlignment(Qt::Alignment); diff --git a/src/widgets/widgets/qlabel_p.h b/src/widgets/widgets/qlabel_p.h index 4458df3b3e..80243239d2 100644 --- a/src/widgets/widgets/qlabel_p.h +++ b/src/widgets/widgets/qlabel_p.h @@ -154,6 +154,7 @@ public: #endif uint openExternalLinks : 1; // <-- space for more bit field values here + QUrlResourceProvider *resourceProvider; friend class QMessageBoxPrivate; }; diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 9fe6e3e1e3..ef1be67975 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -2677,6 +2677,7 @@ void QWidgetTextControl::print(QPagedPaintDevice *printer) const if (!d->cursor.hasSelection()) return; tempDoc = new QTextDocument(const_cast(doc)); + tempDoc->setResourceProvider(doc->resourceProvider()); tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle)); tempDoc->setPageSize(doc->pageSize()); tempDoc->setDefaultFont(doc->defaultFont()); diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index c290305d2f..4e0b7f46df 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include "common.h" // #define DEBUG_WRITE_OUTPUT @@ -192,6 +193,8 @@ private slots: void clearUndoRedoStacks(); void mergeFontFamilies(); + void resourceProvider(); + private: void backgroundImage_checkExpectedHtml(const QTextDocument &doc); void buildRegExpData(); @@ -3593,6 +3596,28 @@ void tst_QTextDocument::clearUndoRedoStacks() QVERIFY(!doc.isUndoAvailable()); } +class UrlResourceProvider : public QUrlResourceProvider +{ +public: + QVariant resource(const QUrl &url) override + { + resourseUrl = url; + return QVariant(); + } + + QUrl resourseUrl; +}; + +void tst_QTextDocument::resourceProvider() +{ + QTextDocument doc; + UrlResourceProvider resourceProvider; + doc.setResourceProvider(&resourceProvider); + QUrl url("test://img"); + doc.setHtml(QStringLiteral("").arg(url.toString())); + doc.resource(QTextDocument::UserResource, url); + QCOMPARE(url, resourceProvider.resourseUrl); +} QTEST_MAIN(tst_QTextDocument) #include "tst_qtextdocument.moc" diff --git a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp index ea72c91b0a..5eed823a34 100644 --- a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp +++ b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include class Widget : public QWidget @@ -103,6 +104,8 @@ private Q_SLOTS: void taskQTBUG_48157_dprPixmap(); void taskQTBUG_48157_dprMovie(); + void resourceProvider(); + private: QLabel *testWidget; QPointer test_box; @@ -596,5 +599,28 @@ void tst_QLabel::taskQTBUG_48157_dprMovie() QCOMPARE(label.sizeHint(), movie.currentPixmap().size() / movie.currentPixmap().devicePixelRatio()); } +class UrlResourceProvider : public QUrlResourceProvider +{ +public: + QVariant resource(const QUrl &url) override + { + resourseUrl = url; + return QVariant(); + } + + QUrl resourseUrl; +}; + +void tst_QLabel::resourceProvider() +{ + QLabel label; + UrlResourceProvider resourceProvider; + label.setResourceProvider(&resourceProvider); + QUrl url("test://img"); + label.setText(QStringLiteral("").arg(url.toString())); + label.show(); + QCOMPARE(url, resourceProvider.resourseUrl); +} + QTEST_MAIN(tst_QLabel) #include "tst_qlabel.moc" -- cgit v1.2.3