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 <ritt.ks@gmail.com>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Alexander Volkov 2018-09-25 18:09:28 +03:00
parent 9e09677c1d
commit 376e3bd8ec
13 changed files with 279 additions and 3 deletions

View File

@ -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

View File

@ -46,6 +46,7 @@
#include "qtextdocumentfragment_p.h"
#include "qtexttable.h"
#include "qtextlist.h"
#include "qurlresourceprovider.h"
#include <qdebug.h>
#if QT_CONFIG(regularexpression)
#include <qregularexpression.h>
@ -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<QTextDocument *>(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.

View File

@ -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<QTextFormat> allFormats() const;
void markContentsDirty(int from, int length);

View File

@ -52,6 +52,7 @@
#include "qtextdocumentlayout_p.h"
#include "qtexttable.h"
#include "qtextengine_p.h"
#include "qurlresourceprovider.h"
#include <stdlib.h>
@ -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;

View File

@ -366,6 +366,7 @@ private:
QMap<int, QTextObject *> objects;
QMap<QUrl, QVariant> resources;
QMap<QUrl, QVariant> cachedResources;
QUrlResourceProvider *resourceProvider;
QString defaultStyleSheet;
int lastBlockCount;

View File

@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2020 Alexander Volkov <avolkov@astralinux.ru>
** 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 <QtCore/qatomic.h>
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<QUrlResourceProvider> 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

View File

@ -0,0 +1,61 @@
/****************************************************************************
**
** Copyright (C) 2020 Alexander Volkov <avolkov@astralinux.ru>
** 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 <QtGui/qtguiglobal.h>
#include <QtCore/qvariant.h>
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

View File

@ -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<QLabel *>(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());

View File

@ -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);

View File

@ -154,6 +154,7 @@ public:
#endif
uint openExternalLinks : 1;
// <-- space for more bit field values here
QUrlResourceProvider *resourceProvider;
friend class QMessageBoxPrivate;
};

View File

@ -2677,6 +2677,7 @@ void QWidgetTextControl::print(QPagedPaintDevice *printer) const
if (!d->cursor.hasSelection())
return;
tempDoc = new QTextDocument(const_cast<QTextDocument *>(doc));
tempDoc->setResourceProvider(doc->resourceProvider());
tempDoc->setMetaInformation(QTextDocument::DocumentTitle, doc->metaInformation(QTextDocument::DocumentTitle));
tempDoc->setPageSize(doc->pageSize());
tempDoc->setDefaultFont(doc->defaultFont());

View File

@ -47,6 +47,7 @@
#include <qimage.h>
#include <qtextlayout.h>
#include <QDomDocument>
#include <qurlresourceprovider.h>
#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("<img src='%1'/>").arg(url.toString()));
doc.resource(QTextDocument::UserResource, url);
QCOMPARE(url, resourceProvider.resourseUrl);
}
QTEST_MAIN(tst_QTextDocument)
#include "tst_qtextdocument.moc"

View File

@ -40,6 +40,7 @@
#include <qmessagebox.h>
#include <qfontmetrics.h>
#include <qmath.h>
#include <qurlresourceprovider.h>
#include <private/qlabel_p.h>
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<Widget> 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("<img src='%1'/>").arg(url.toString()));
label.show();
QCOMPARE(url, resourceProvider.resourseUrl);
}
QTEST_MAIN(tst_QLabel)
#include "tst_qlabel.moc"