QML: Allow conversion from QV4::Sequence to different iterable
Pick-to: 6.5 Fixes: QTBUG-112291 Change-Id: Idd47ea8daf9c54759af6c1feba68bd52d1163615 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
4307755655
commit
1b89c1edca
|
@ -2444,6 +2444,23 @@ void ExecutionEngine::setExtensionData(int index, Deletable *data)
|
|||
m_extensionData[index] = data;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
bool convertToIterable(QMetaType metaType, void *data, Source *sequence)
|
||||
{
|
||||
QSequentialIterable iterable;
|
||||
if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
|
||||
return false;
|
||||
|
||||
const QMetaType elementMetaType = iterable.valueMetaType();
|
||||
QVariant element(elementMetaType);
|
||||
for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
|
||||
if (!ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()))
|
||||
element = QVariant(elementMetaType);
|
||||
iterable.addValue(element, QSequentialIterable::AtEnd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converts a JS value to a meta-type.
|
||||
// data must point to a place that can store a value of the given type.
|
||||
// Returns true if conversion succeeded, false otherwise.
|
||||
|
@ -2711,21 +2728,14 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi
|
|||
metaType.construct(data, result.constData());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (convertToIterable(metaType, data, sequence))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (const QV4::ArrayObject *array = value.as<ArrayObject>()) {
|
||||
QSequentialIterable iterable;
|
||||
if (QMetaType::view(
|
||||
metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable)) {
|
||||
const QMetaType elementMetaType = iterable.valueMetaType();
|
||||
QVariant element(elementMetaType);
|
||||
for (qsizetype i = 0, end = array->getLength(); i < end; ++i) {
|
||||
if (!metaTypeFromJS(array->get(i), elementMetaType, element.data()))
|
||||
element = QVariant(elementMetaType);
|
||||
iterable.addValue(element, QSequentialIterable::AtEnd);
|
||||
}
|
||||
if (convertToIterable(metaType, data, array))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -15,6 +15,7 @@ set(cpp_sources
|
|||
multiforeign.h
|
||||
objectwithmethod.h
|
||||
person.cpp person.h
|
||||
sequenceToIterable.h
|
||||
sequencetypeexample.cpp sequencetypeexample.h
|
||||
state.h
|
||||
theme.cpp theme.h
|
||||
|
@ -184,6 +185,7 @@ set(qml_files
|
|||
scopeVsObject.qml
|
||||
script.js
|
||||
script.mjs
|
||||
sequenceToIterable.qml
|
||||
shadowedMethod.qml
|
||||
shared/Slider.qml
|
||||
shifts.qml
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#ifndef SEQUENCETOITERABLE_H
|
||||
#define SEQUENCETOITERABLE_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtQml/qqml.h>
|
||||
|
||||
class Entry : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Entry(const QString &name, QObject *parent = nullptr)
|
||||
: QObject(parent), m_name(name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
class EntryWrapper {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Entry)
|
||||
QML_NAMED_ELEMENT(Entry)
|
||||
QML_UNCREATABLE("These are my Entry objects")
|
||||
};
|
||||
|
||||
class EntryListRegistration
|
||||
{
|
||||
Q_GADGET
|
||||
QML_FOREIGN(QList<Entry*>)
|
||||
QML_ANONYMOUS
|
||||
QML_SEQUENTIAL_CONTAINER(Entry*)
|
||||
};
|
||||
|
||||
class EntrySource : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
explicit EntrySource(QObject* parent = nullptr) : QObject(parent) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
m_entries.push_back(new Entry(QString("Item %1").arg(i), this));
|
||||
}
|
||||
}
|
||||
Q_INVOKABLE QList<Entry*> getEntries() const { return m_entries; }
|
||||
|
||||
private:
|
||||
QList<Entry*> m_entries;
|
||||
};
|
||||
|
||||
#endif // SEQUENCETOITERABLE_H
|
|
@ -0,0 +1,19 @@
|
|||
pragma Strict
|
||||
import QtQuick
|
||||
import TestTypes
|
||||
|
||||
Item {
|
||||
Component.onCompleted: () => {
|
||||
repeater.model = EntrySource.getEntries()
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
Item {
|
||||
required property int index
|
||||
required property QtObject modelData
|
||||
}
|
||||
}
|
||||
|
||||
property int c: children.length
|
||||
}
|
|
@ -154,6 +154,7 @@ private slots:
|
|||
void revisions();
|
||||
void scopeObjectDestruction();
|
||||
void scopeVsObject();
|
||||
void sequenceToIterable();
|
||||
void shadowedMethod();
|
||||
void shifts();
|
||||
void signalHandler();
|
||||
|
@ -3255,6 +3256,16 @@ void tst_QmlCppCodegen::scopeVsObject()
|
|||
QCOMPARE(object->property("objectName").toString(), u"foobar"_s);
|
||||
}
|
||||
|
||||
void tst_QmlCppCodegen::sequenceToIterable()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/sequenceToIterable.qml"_s));
|
||||
QVERIFY2(!component.isError(), component.errorString().toUtf8());
|
||||
QScopedPointer<QObject> object(component.create());
|
||||
QVERIFY(!object.isNull());
|
||||
QCOMPARE(object->property("c").toInt(), 11);
|
||||
}
|
||||
|
||||
void tst_QmlCppCodegen::shadowedMethod()
|
||||
{
|
||||
QQmlEngine e;
|
||||
|
|
Loading…
Reference in New Issue