Skip to content

Commit

Permalink
Added support for custom enum types that are used as flags
Browse files Browse the repository at this point in the history
It creates a checkbox for each flag. They will need to be able to
collapse in case there are lots of flags, but this works for now.
  • Loading branch information
bjorn committed Sep 18, 2024
1 parent db138cc commit f77fb26
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 43 deletions.
14 changes: 9 additions & 5 deletions src/tiled/propertieswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2126,18 +2126,22 @@ void PropertiesWidget::currentObjectChanged(Object *object)
case PropertyType::PT_Class:
// todo: class values
break;
case PropertyType::PT_Enum:
auto enumType = static_cast<const EnumPropertyType&>(*propertyType);
// todo: support valuesAsFlags
property = new EnumProperty<int>(
case PropertyType::PT_Enum: {
auto enumProperty = new BaseEnumProperty(
name,
[object, name] { return object->property(name).value<PropertyValue>().value.toInt(); },
[=](int value) {
mDocument->undoStack()->push(new SetProperty(mDocument, { object }, name, propertyType->wrap(value)));
});
static_cast<EnumProperty<int>*>(property)->setEnumData(enumType.values);

auto enumType = static_cast<const EnumPropertyType&>(*propertyType);
enumProperty->setEnumData(enumType.values);
enumProperty->setFlags(enumType.valuesAsFlags);

property = enumProperty;
break;
}
}
}
}
break;
Expand Down
63 changes: 52 additions & 11 deletions src/tiled/varianteditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,34 +609,75 @@ QWidget *VariantEditor::createEditor(Property *property)
}


QWidget *createEnumEditor(IntProperty *property, const EnumData &enumData, QWidget *parent)
QWidget *BaseEnumProperty::createEnumEditor(QWidget *parent)
{
auto editor = new QComboBox(parent);
// This allows the combo box to shrink horizontally.
editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);

for (qsizetype i = 0; i < enumData.names.size(); ++i) {
auto value = enumData.values.isEmpty() ? i : enumData.values.value(i);
editor->addItem(enumData.icons[value],
enumData.names[i],
for (qsizetype i = 0; i < m_enumData.names.size(); ++i) {
auto value = m_enumData.values.value(i, i);
editor->addItem(m_enumData.icons[value],
m_enumData.names[i],
value);
}

auto syncEditor = [property, editor] {
auto syncEditor = [this, editor] {
const QSignalBlocker blocker(editor);
editor->setCurrentIndex(editor->findData(property->value()));
editor->setCurrentIndex(editor->findData(value()));
};
syncEditor();

QObject::connect(property, &Property::valueChanged, editor, syncEditor);
QObject::connect(editor, qOverload<int>(&QComboBox::currentIndexChanged), property,
[editor, property] {
property->setValue(editor->currentData().toInt());
QObject::connect(this, &Property::valueChanged, editor, syncEditor);
QObject::connect(editor, qOverload<int>(&QComboBox::currentIndexChanged), this,
[editor, this] {
setValue(editor->currentData().toInt());
});

return editor;
}

QWidget *BaseEnumProperty::createFlagsEditor(QWidget *parent)
{
auto editor = new QWidget(parent);
auto layout = new QVBoxLayout(editor);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);

for (qsizetype i = 0; i < m_enumData.names.size(); ++i) {
auto checkBox = new QCheckBox(m_enumData.names[i], editor);
layout->addWidget(checkBox);

QObject::connect(checkBox, &QCheckBox::toggled, this, [=](bool checked) {
const auto enumItemValue = m_enumData.values.value(i, 1 << i);
int flags = value();
if (checked)
flags |= enumItemValue;
else
flags &= ~enumItemValue;
setValue(flags);
});
}

auto syncEditor = [=] {
for (int i = 0; i < layout->count(); ++i) {
auto checkBox = qobject_cast<QCheckBox *>(layout->itemAt(i)->widget());
if (checkBox) {
const auto enumItemValue = m_enumData.values.value(i, 1 << i);

QSignalBlocker blocker(checkBox);
checkBox->setChecked((value() & enumItemValue) == enumItemValue);
}
}
};

syncEditor();

QObject::connect(this, &Property::valueChanged, editor, syncEditor);

return editor;
}

Property *PropertyFactory::createQObjectProperty(QObject *qObject,
const char *propertyName,
const QString &displayName)
Expand Down
66 changes: 39 additions & 27 deletions src/tiled/varianteditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ class PropertyFactory

struct EnumData
{
EnumData(const QStringList &names,
EnumData(const QStringList &names = {},
const QList<int> &values = {},
const QMap<int, QIcon> &icons = {})
: names(names)
Expand All @@ -344,44 +344,56 @@ EnumData enumData()
return {{}};
}

QWidget *createEnumEditor(IntProperty *property,
const EnumData &enumData,
QWidget *parent);
/**
* A property that wraps an integer value and creates either a combo box or a
* list of checkboxes based on the given EnumData.
*/
class BaseEnumProperty : public IntProperty
{
Q_OBJECT

public:
using IntProperty::IntProperty;

void setEnumData(const EnumData &enumData) { m_enumData = enumData; }
void setFlags(bool flags) { m_flags = flags; }

QWidget *createEditor(QWidget *parent) override
{
return m_flags ? createFlagsEditor(parent)
: createEnumEditor(parent);
}

protected:
QWidget *createFlagsEditor(QWidget *parent);
QWidget *createEnumEditor(QWidget *parent);

EnumData m_enumData;
bool m_flags = false;
};

/**
* A property that wraps an enum value and creates a combo box based on the
* given EnumData.
* A property that wraps an enum value and automatically sets the EnumData
* based on the given type.
*/
template <typename Enum>
class EnumProperty : public IntProperty
class EnumProperty : public BaseEnumProperty
{
public:
EnumProperty(const QString &name,
std::function<Enum()> get,
std::function<void(Enum)> set,
QObject *parent = nullptr)
: IntProperty(name,
[get] {
return static_cast<int>(get());
},
set ? [set](const int &value){ set(static_cast<Enum>(value)); }
: std::function<void(const int&)>(),
parent)
, m_enumData(enumData<Enum>())
{}

void setEnumData(const EnumData &enumData)
{
m_enumData = enumData;
}

QWidget *createEditor(QWidget *parent) override
: BaseEnumProperty(name,
[get] {
return static_cast<int>(get());
},
set ? [set](const int &value){ set(static_cast<Enum>(value)); }
: std::function<void(const int&)>(),
parent)
{
return createEnumEditor(this, m_enumData, parent);
setEnumData(enumData<Enum>());
}

private:
EnumData m_enumData;
};


Expand Down

0 comments on commit f77fb26

Please sign in to comment.