mardi 6 août 2019

What is the most generic way to link two independent classes without extra code?

There's a hierarchy of classes describing different properties of some object. The abstract class Property is a base class, and it has children: IntegerProperty, BooleanProperty, and so on. All data is encoded in QString and derived classes decode it in their own way.

class Property : public QObject
{
    Q_OBJECT

public:
    // ...
    virtual QString value() const = 0;
    virtual bool setValue(const QString &value) = 0;
    virtual bool validateValue(const QString& value) = 0;
    // ...
};

class IntegerProperty : public Property
{
    // ...
    virtual QString value() const override;
    virtual bool setValue(const QString &value) override;
    virtual bool validateValue(const QString& value) override;
    // ...
};

// ...

Every property class must have an independent editor (GUI widget) - PropertyEditor (abstract class again), IntegerPropertyEditor, BooleanPropertyEditor, and so on.

class PropertyEditor : public QWidget
{
    Q_OBJECT

public:
    inline Facer::PropertyPointer attachedProperty() { return m_property; }

protected:
    PropertyEditor(Facer::PropertyPointer attachedProperty, QWidget* parent = nullptr);

    virtual void mousePressEvent(QMouseEvent* event) override;
    virtual bool eventFilter(QObject *watched, QEvent *event) override;

    // ...
};

class IntegerPropertyEditor : public PropertyEditor
{
   // ...
};

// ...

For example, I have a set of different properties. I don't know which exactly properties I have because they are all pointers to Property class. My task is to create specified editors of these properties, so I need to get IntegerPropertyEditor if the property object is IntegerProperty.

for (Property* property : propertySet())
    PropertyEditor* editor = createEditor(property);

I made a temporary workaround with macro:

#define IF_TYPE_GET_EDITOR(propertyType, editorType) \
    if (std::dynamic_pointer_cast<propertyType>(property)) \
        return new editorType(property, this);

// ...

PropertyEditor *PropertySetWidget::create(PropertyPointer property)
{
    IF_TYPE_GET_EDITOR(BooleanProperty, BooleanPropertyEditor)
    else IF_TYPE_GET_EDITOR(ColorProperty, ColorPropertyEditor)
    else IF_TYPE_GET_EDITOR(FloatingPointProperty, FloatingPointPropertyEditor)
    else IF_TYPE_GET_EDITOR(FontProperty, FontPropertyEditor)
    else IF_TYPE_GET_EDITOR(IntegerProperty, IntegerPropertyEditor)
    else IF_TYPE_GET_EDITOR(TextProperty, TextPropertyEditor)
    else throw std::runtime_error("This PropertyType is not implemented yet");
}

It doesn't look like a good solution - if I add a new type of property and its editor, I'll have to update this code as well. What is the most convenient and generic way to link an editor class and a property class?

Aucun commentaire:

Enregistrer un commentaire