Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for list property types #4002

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class MapReaderPrivate

Properties readProperties();
void readProperty(Properties *properties, const ExportContext &context);
QVariant readPropertyValue(const ExportContext &context);

MapReader *p;

Expand Down Expand Up @@ -1448,13 +1449,22 @@ void MapReaderPrivate::readProperty(Properties *properties, const ExportContext
const QXmlStreamAttributes atts = xml.attributes();
QString propertyName = atts.value(QLatin1String("name")).toString();

properties->insert(propertyName, readPropertyValue(context));
}

QVariant MapReaderPrivate::readPropertyValue(const ExportContext &context)
{
const QXmlStreamAttributes atts = xml.attributes();

ExportValue exportValue;
exportValue.typeName = atts.value(QLatin1String("type")).toString();
exportValue.propertyTypeName = atts.value(QLatin1String("propertytype")).toString();

const QString propertyValue = atts.value(QLatin1String("value")).toString();
exportValue.value = propertyValue;

QVariantList values;

while (xml.readNext() != QXmlStreamReader::Invalid) {
if (xml.isEndElement()) {
break;
Expand All @@ -1464,12 +1474,17 @@ void MapReaderPrivate::readProperty(Properties *properties, const ExportContext
} else if (xml.isStartElement()) {
if (xml.name() == QLatin1String("properties"))
exportValue.value = readProperties();
else if (xml.name() == QLatin1String("item"))
values.append(readPropertyValue(context));
else
readUnknownElement();
}
}

properties->insert(propertyName, context.toPropertyValue(exportValue));
if (exportValue.typeName == QLatin1String("list"))
exportValue.value = values;

return context.toPropertyValue(exportValue);
}


Expand Down
39 changes: 28 additions & 11 deletions src/libtiled/maptovariantconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,27 +807,44 @@ void MapToVariantConverter::addProperties(QVariantMap &variantMap,
// TODO: Support custom property types in json1? Maybe with a customPropertyTypesMap...
}

variantMap[QStringLiteral("properties")] = propertiesMap;
variantMap[QStringLiteral("propertytypes")] = propertyTypesMap;
variantMap[QStringLiteral("properties")] = std::move(propertiesMap);
variantMap[QStringLiteral("propertytypes")] = std::move(propertyTypesMap);
} else {
QVariantList propertiesVariantList;

Properties::const_iterator it = properties.constBegin();
Properties::const_iterator it_end = properties.constEnd();
for (; it != it_end; ++it) {
const auto exportValue = context.toExportValue(it.value());

QVariantMap propertyVariantMap;
QVariantMap propertyVariantMap = toVariantMap(it.value(), context);
propertyVariantMap[QStringLiteral("name")] = it.key();
propertyVariantMap[QStringLiteral("value")] = exportValue.value;
propertyVariantMap[QStringLiteral("type")] = exportValue.typeName;

if (!exportValue.propertyTypeName.isEmpty())
propertyVariantMap[QStringLiteral("propertytype")] = exportValue.propertyTypeName;

propertiesVariantList << propertyVariantMap;
propertiesVariantList << std::move(propertyVariantMap);
}

variantMap[QStringLiteral("properties")] = propertiesVariantList;
}
}

QVariantMap MapToVariantConverter::toVariantMap(const QVariant &propertyValue, const ExportContext &context) const
{
const auto exportValue = context.toExportValue(propertyValue);
QVariantMap propertyVariantMap;

propertyVariantMap[QStringLiteral("type")] = exportValue.typeName;
if (!exportValue.propertyTypeName.isEmpty())
propertyVariantMap[QStringLiteral("propertytype")] = exportValue.propertyTypeName;

if (exportValue.typeName == QLatin1String("list")) {
QVariantList valuesVariantList;

const QVariantList values = propertyValue.toList();
for (const QVariant &value : values)
valuesVariantList << toVariantMap(value, context);

propertyVariantMap[QStringLiteral("value")] = std::move(valuesVariantList);
} else {
propertyVariantMap[QStringLiteral("value")] = exportValue.value;
}

return propertyVariantMap;
}
1 change: 1 addition & 0 deletions src/libtiled/maptovariantconverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class TILEDSHARED_EXPORT MapToVariantConverter

void addProperties(QVariantMap &variantMap,
const Properties &properties) const;
QVariantMap toVariantMap(const QVariant &propertyValue, const ExportContext &context) const;

int mVersion;
QDir mDir;
Expand Down
62 changes: 41 additions & 21 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class MapWriterPrivate
void writeGroupLayer(QXmlStreamWriter &w, const GroupLayer &groupLayer);
void writeProperties(QXmlStreamWriter &w,
const Properties &properties);
void writeExportValue(QXmlStreamWriter &w,
const QVariant &value, const ExportContext &context);
void writeImage(QXmlStreamWriter &w,
const QUrl &source,
const QPixmap &image,
Expand Down Expand Up @@ -899,30 +901,48 @@ void MapWriterPrivate::writeProperties(QXmlStreamWriter &w,
w.writeStartElement(QStringLiteral("property"));
w.writeAttribute(QStringLiteral("name"), it.key());

const auto exportValue = context.toExportValue(it.value());
if (exportValue.typeName != QLatin1String("string"))
w.writeAttribute(QStringLiteral("type"), exportValue.typeName);
if (!exportValue.propertyTypeName.isEmpty())
w.writeAttribute(QStringLiteral("propertytype"), exportValue.propertyTypeName);

// For class property values, write out the original value, so that the
// propertytype attribute can also be written for their members where
// applicable.
if (exportValue.value.userType() == QMetaType::QVariantMap) {
writeProperties(w, it.value().value<PropertyValue>().value.toMap());
} else {
const QString value = exportValue.value.toString();

if (value.contains(QLatin1Char('\n')))
w.writeCharacters(value);
else
w.writeAttribute(QStringLiteral("value"), value);
}
writeExportValue(w, it.value(), context);

w.writeEndElement();
w.writeEndElement(); // </property>
}

w.writeEndElement();
w.writeEndElement(); // </properties>
}

void MapWriterPrivate::writeExportValue(QXmlStreamWriter &w,
const QVariant &value,
const ExportContext &context)
{
const auto exportValue = context.toExportValue(value);
if (exportValue.typeName != QLatin1String("string"))
w.writeAttribute(QStringLiteral("type"), exportValue.typeName);
if (!exportValue.propertyTypeName.isEmpty())
w.writeAttribute(QStringLiteral("propertytype"), exportValue.propertyTypeName);

switch (exportValue.value.userType()) {
case QMetaType::QVariantList: {
const auto values = exportValue.value.toList();
for (const QVariant &value : values) {
w.writeStartElement(QStringLiteral("item"));
writeExportValue(w, value, context);
w.writeEndElement(); // </item>
}
break;
}
case QMetaType::QVariantMap:
// Write out the original value, so that the propertytype attribute
// can also be written for their members where applicable.
writeProperties(w, value.value<PropertyValue>().value.toMap());
break;
default:
const QString value = exportValue.value.toString();

if (value.contains(QLatin1Char('\n')))
w.writeCharacters(value);
else
w.writeAttribute(QStringLiteral("value"), value);
break;
}
}

void MapWriterPrivate::writeImage(QXmlStreamWriter &w,
Expand Down
2 changes: 2 additions & 0 deletions src/libtiled/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ class TILEDSHARED_EXPORT Object

/**
* Returns the type of the object's \a name property, as a string.
*
* This function exists only for the Python plugin.
*/
QString propertyType(const QString &name) const
{ return typeName(mProperties.value(name)); }
Expand Down
60 changes: 45 additions & 15 deletions src/libtiled/properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,21 +196,6 @@ void aggregateProperties(AggregatedProperties &aggregated, const Properties &pro
}
}

int propertyValueId()
{
return qMetaTypeId<PropertyValue>();
}

int filePathTypeId()
{
return qMetaTypeId<FilePath>();
}

int objectRefTypeId()
{
return qMetaTypeId<ObjectRef>();
}

QString typeToName(int type)
{
// We can't handle the PropertyValue purely by its type ID, since we need to
Expand All @@ -226,6 +211,8 @@ QString typeToName(int type)
return QStringLiteral("color");
case QMetaType::QVariantMap:
return QStringLiteral("class");
case QMetaType::QVariantList:
return QStringLiteral("list");

default:
if (type == filePathTypeId())
Expand All @@ -250,6 +237,8 @@ static int nameToType(const QString &name)
return objectRefTypeId();
if (name == QLatin1String("class"))
return QMetaType::QVariantMap;
if (name == QLatin1String("list"))
return QMetaType::QVariantList;

return QVariant::nameToType(name.toLatin1().constData());
}
Expand All @@ -262,6 +251,14 @@ QString typeName(const QVariant &value)
return typeToName(value.userType());
}

QString userTypeName(const QVariant &value)
{
if (value.userType() == propertyValueId())
return value.value<PropertyValue>().typeName();

return typeToName(value.userType());
}

const PropertyType *PropertyValue::type() const
{
return Object::propertyTypes().findTypeById(typeId);
Expand Down Expand Up @@ -309,6 +306,7 @@ ExportValue ExportContext::toExportValue(const QVariant &value) const
} else if (metaType == objectRefTypeId()) {
exportValue.value = ObjectRef::toInt(value.value<ObjectRef>());
} else {
// Other values, including lists, do not need special handling here
exportValue.value = value;
}

Expand Down Expand Up @@ -343,6 +341,9 @@ QVariant ExportContext::toPropertyValue(const QVariant &value, int metaType) con
if (metaType == QMetaType::QVariantMap || metaType == propertyValueId())
return value; // should be covered by property type

if (metaType == QMetaType::QVariantList)
return value; // list elements should be converted individually

if (metaType == filePathTypeId()) {
const QUrl url = toUrl(value.toString(), mPath);
return QVariant::fromValue(FilePath { url });
Expand All @@ -365,6 +366,35 @@ void initializeMetatypes()
QMetaType::registerConverter<QString, FilePath>(&FilePath::fromString);
}

QVariantList possiblePropertyValues(const ClassPropertyType *parentClassType)
{
QVariantList values;

values.append(false); // bool
values.append(QColor()); // color
values.append(0.0); // float
values.append(QVariant::fromValue(FilePath())); // file
values.append(0); // int
values.append(QVariant::fromValue(ObjectRef())); // object
values.append(QString()); // string
values.append(QVariant(QVariantList())); // list

for (const auto propertyType : Object::propertyTypes()) {
// Avoid suggesting the creation of circular dependencies between types
if (parentClassType && !parentClassType->canAddMemberOfType(propertyType))
continue;

// Avoid suggesting classes not meant to be used as property value
if (propertyType->isClass())
if (!static_cast<const ClassPropertyType*>(propertyType)->isPropertyValueType())
continue;

values.append(propertyType->wrap(propertyType->defaultValue()));
}

return values;
}

} // namespace Tiled

#include "moc_properties.cpp"
11 changes: 7 additions & 4 deletions src/libtiled/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class TILEDSHARED_EXPORT ObjectRef
Q_PROPERTY(int id MEMBER id)

public:
int id;
int id = 0;

bool operator==(const ObjectRef &o) const
{ return id == o.id; }
Expand Down Expand Up @@ -185,13 +185,16 @@ TILEDSHARED_EXPORT void mergeProperties(Properties &target, const Properties &so
TILEDSHARED_EXPORT QJsonArray propertiesToJson(const Properties &properties, const ExportContext &context = ExportContext());
TILEDSHARED_EXPORT Properties propertiesFromJson(const QJsonArray &json, const ExportContext &context = ExportContext());

TILEDSHARED_EXPORT int propertyValueId();
TILEDSHARED_EXPORT int filePathTypeId();
TILEDSHARED_EXPORT int objectRefTypeId();
constexpr int propertyValueId() { return qMetaTypeId<PropertyValue>(); }
constexpr int filePathTypeId() { return qMetaTypeId<FilePath>(); }
constexpr int objectRefTypeId() { return qMetaTypeId<ObjectRef>(); }

TILEDSHARED_EXPORT QString typeToName(int type);
TILEDSHARED_EXPORT QString typeName(const QVariant &value);
TILEDSHARED_EXPORT QString userTypeName(const QVariant &value);

TILEDSHARED_EXPORT void initializeMetatypes();

TILEDSHARED_EXPORT QVariantList possiblePropertyValues(const ClassPropertyType *parentClassType = nullptr);

} // namespace Tiled
21 changes: 20 additions & 1 deletion src/libtiled/varianttomapconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,26 @@ Properties VariantToMapConverter::toProperties(const QVariant &propertiesVariant
exportValue.typeName = propertyVariantMap[QStringLiteral("type")].toString();
exportValue.propertyTypeName = propertyVariantMap[QStringLiteral("propertytype")].toString();

properties[propertyName] = context.toPropertyValue(exportValue);
auto &value = properties[propertyName];

if (exportValue.typeName == QLatin1String("list")) {
const QVariantList values = exportValue.value.toList();
QVariantList convertedList;
convertedList.reserve(values.size());
for (const QVariant &value : values) {
const QVariantMap valueVariantMap = value.toMap();
ExportValue itemValue;
itemValue.value = valueVariantMap[QStringLiteral("value")];
itemValue.typeName = valueVariantMap[QStringLiteral("type")].toString();
itemValue.propertyTypeName = valueVariantMap[QStringLiteral("propertytype")].toString();

// todo: this doesn't support lists of lists
convertedList.append(context.toPropertyValue(itemValue));
}
value = convertedList;
} else {
value = context.toPropertyValue(exportValue);
}
}

return properties;
Expand Down
Loading