ListModel: support URLs

Fixes: QTBUG-88379
Change-Id: I6e2ea550d8f8972c5fdcdc21a5e3851992c591a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2020-11-10 16:50:46 +01:00
parent 6a605df673
commit bf547fb272
4 changed files with 127 additions and 3 deletions

View File

@ -50,6 +50,7 @@
#include <private/qv4object_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4urlobject_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
#include <private/qv4lookup_p.h>
@ -89,7 +90,7 @@ static QString roleTypeName(ListLayout::Role::DataType t)
static const QString roleTypeNames[] = {
QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"),
QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"),
QStringLiteral("DateTime"), QStringLiteral("Function")
QStringLiteral("DateTime"), QStringLiteral("Url"), QStringLiteral("Function")
};
if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
@ -128,8 +129,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data
const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
{
const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QUrl), sizeof(QJSValue) };
const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QUrl), sizeof(QJSValue) };
Role *r = new Role;
r->name = key;
@ -226,6 +227,7 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV
case QMetaType::QString: type = Role::String; break;
case QMetaType::QVariantMap: type = Role::VariantMap; break;
case QMetaType::QDateTime: type = Role::DateTime; break;
case QMetaType::QUrl: type = Role::Url; break;
default: {
if (data.userType() == qMetaTypeId<QJSValue>() &&
data.value<QJSValue>().isCallable()) {
@ -587,6 +589,10 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
QDateTime dt = dd->toQDateTime();
roleIndex = e->setDateTimeProperty(r, dt);
} else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
QUrl qurl = QUrl(url->href());
roleIndex = e->setUrlProperty(r, qurl);
} else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function);
QV4::ScopedFunctionObject func(scope, f);
@ -599,6 +605,11 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
if (role.type == ListLayout::Role::QObject)
roleIndex = e->setQObjectProperty(role, o);
} else if (QVariant maybeUrl = o->engine()->toVariant(o->asReturnedValue(), QMetaType::QUrl, true);
maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
QUrl qurl = maybeUrl.toUrl();
roleIndex = e->setUrlProperty(r, qurl);
} else {
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
if (role.type == ListLayout::Role::VariantMap) {
@ -673,6 +684,12 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
QDateTime dt = date->toQDateTime();
e->setDateTimePropertyFast(r, dt);
}
} else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
if (r.type == ListLayout::Role::Url) {
QUrl qurl = QUrl(url->href()); // does what the private UrlObject->toQUrl would do
e->setUrlPropertyFast(r, qurl);
}
} else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
QObject *o = wrapper->object();
@ -680,6 +697,15 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement
if (r.type == ListLayout::Role::QObject)
e->setQObjectPropertyFast(r, o);
} else {
QVariant maybeUrl = o->engine()->toVariant(o->asReturnedValue(), QMetaType::QUrl, true);
if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
const QUrl qurl = maybeUrl.toUrl();
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
if (r.type == ListLayout::Role::Url) {
e->setUrlPropertyFast(r, qurl);
}
return;
}
const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
if (role.type == ListLayout::Role::VariantMap)
e->setVariantMapFast(role, o);
@ -823,6 +849,17 @@ QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role)
return dt;
}
QUrl *ListElement::getUrlProperty(const ListLayout::Role &role)
{
QUrl *url = nullptr;
char *mem = getPropertyMemory(role);
if (isMemoryUsed<QUrl>(mem))
url = reinterpret_cast<QUrl *>(mem);
return url;
}
QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role)
{
QJSValue *f = nullptr;
@ -927,6 +964,14 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
}
}
break;
case ListLayout::Role::Url:
{
if (isMemoryUsed<QUrl>(mem)) {
QUrl *url = reinterpret_cast<QUrl *>(mem);
data = *url;
}
}
break;
case ListLayout::Role::Function:
{
if (isMemoryUsed<QJSValue>(mem)) {
@ -1099,6 +1144,23 @@ int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTi
return roleIndex;
}
int ListElement::setUrlProperty(const ListLayout::Role &role, const QUrl &url)
{
int roleIndex = -1;
if (role.type == ListLayout::Role::Url) {
char *mem = getPropertyMemory(role);
if (isMemoryUsed<QUrl>(mem)) {
QUrl *qurl = reinterpret_cast<QUrl *>(mem);
qurl->~QUrl();
}
new (mem) QUrl(url);
roleIndex = role.index;
}
return roleIndex;
}
int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f)
{
int roleIndex = -1;
@ -1177,6 +1239,12 @@ void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QD
new (mem) QDateTime(dt);
}
void ListElement::setUrlPropertyFast(const ListLayout::Role &role, const QUrl &url)
{
char *mem = getPropertyMemory(role);
new (mem) QUrl(url);
}
void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f)
{
char *mem = getPropertyMemory(role);
@ -1204,6 +1272,9 @@ void ListElement::clearProperty(const ListLayout::Role &role)
case ListLayout::Role::DateTime:
setDateTimeProperty(role, QDateTime());
break;
case ListLayout::Role::Url:
setUrlProperty(role, QUrl());
break;
case ListLayout::Role::VariantMap:
setVariantMapProperty(role, (QVariantMap *)nullptr);
break;
@ -1336,6 +1407,13 @@ void ListElement::destroy(ListLayout *layout)
dt->~QDateTime();
}
break;
case ListLayout::Role::Url:
{
QUrl *url = getUrlProperty(r);
if (url)
url->~QUrl();
break;
}
case ListLayout::Role::Function:
{
QJSValue *f = getFunctionProperty(r);
@ -1388,6 +1466,9 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant
case ListLayout::Role::DateTime:
roleIndex = setDateTimeProperty(role, d.toDateTime());
break;
case ListLayout::Role::Url:
roleIndex = setUrlProperty(role, d.toUrl());
break;
case ListLayout::Role::Function:
roleIndex = setFunctionProperty(role, d.value<QJSValue>());
break;
@ -1433,6 +1514,10 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
QV4::Scoped<QV4::DateObject> dd(scope, d);
QDateTime dt = dd->toQDateTime();
roleIndex = setDateTimeProperty(role, dt);
} else if (d.as<QV4::UrlObject>()) {
QV4::Scoped<QV4::UrlObject> url(scope, d);
QUrl qurl = QUrl(url->href());
roleIndex = setUrlProperty(role, qurl);
} else if (d.as<QV4::FunctionObject>()) {
QV4::ScopedFunctionObject f(scope, d);
QJSValue jsv;
@ -1446,6 +1531,11 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
roleIndex = setQObjectProperty(role, o);
} else if (role.type == ListLayout::Role::VariantMap) {
roleIndex = setVariantMapProperty(role, o);
} else if (role.type == ListLayout::Role::Url) {
QVariant maybeUrl = o->engine()->toVariant(o.asReturnedValue(), QMetaType::QUrl, true);
if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
roleIndex = setUrlProperty(role, maybeUrl.toUrl());
}
}
} else if (d.isNullOrUndefined()) {
clearProperty(role);

View File

@ -216,6 +216,7 @@ public:
QObject,
VariantMap,
DateTime,
Url,
Function,
MaxDataType
@ -305,6 +306,7 @@ private:
int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o);
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
int setUrlProperty(const ListLayout::Role &role, const QUrl &url);
int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f);
int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b);
@ -315,6 +317,7 @@ private:
void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o);
void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
void setUrlPropertyFast(const ListLayout::Role &role, const QUrl &url);
void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f);
void clearProperty(const ListLayout::Role &role);
@ -326,6 +329,7 @@ private:
QPointer<QObject> *getGuardProperty(const ListLayout::Role &role);
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
QDateTime *getDateTimeProperty(const ListLayout::Role &role);
QUrl *getUrlProperty(const ListLayout::Role &role);
QJSValue *getFunctionProperty(const ListLayout::Role &role);
inline char *getPropertyMemory(const ListLayout::Role &role);

View File

@ -0,0 +1,19 @@
import QtQuick 2
import QtQml.Models 2
Item {
id: root
readonly property url url1: "http://qt-project.org"
property var result1
property var result2
ListModel {id: myModel}
Component.onCompleted: {
myModel.append({"url": new URL("http://qt.io")})
myModel.append({"url": url1})
const entry1 = myModel.get(0)
root.result1 = entry1.url;
const entry2 = myModel.get(1)
root.result2 = entry2.url;
}
}

View File

@ -121,6 +121,7 @@ private slots:
void empty_element_warning_data();
void datetime();
void datetime_data();
void url();
void about_to_be_signals();
void modify_through_delegate();
void bindingsOnGetResult();
@ -1392,6 +1393,16 @@ void tst_qqmllistmodel::datetime_data()
QTest::newRow("dt4") << "{append({'date':dt0});setProperty(0,'date',dt1);get(0).date}" << dt1;
}
void tst_qqmllistmodel::url()
{
QQmlEngine engine;
QQmlComponent comp(&engine, testFileUrl("urls.qml"));
QScopedPointer<QObject> o {comp.create()};
QVERIFY(o);
QCOMPARE(o->property("result1").toUrl(), QUrl("http://qt.io"));
QCOMPARE(o->property("result2").toUrl(), QUrl("http://qt-project.org"));
}
void tst_qqmllistmodel::datetime()
{
QFETCH(QString, qml);