property.hpp 11 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
\file property.hpp
\date 17/04/2021
\author Yann Boucher
\version 1
\brief Property

Fichier définissant la classe Property représentant une propriété chargable, sauvegardable et affichable d'un objet.

**/


#ifndef PROPERTY_HPP
#define PROPERTY_HPP

#include <string>
#include <vector>
#include <memory>
#include <limits>
#include <exception>
21
#include <cassert>
22
23
24
25
26
27
28
29
30
31
32
33
34

#include <QWidget>
#include <QLayout>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QMessageBox>
#include <QFormLayout>

#include <QJsonDocument>
#include <QJsonObject>
#include <QFile>

35
#include "coord.hpp"
36
#include "neighborhood.hpp"
37
#include "neighborhoodDialog.hpp"
38

39
struct HelpProperty;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
struct StringProperty;
struct IntegerProperty;
struct CoordinateProperty;
struct PropertyList;

/**
\class PropertyVisitor
\brief Représente un visiteur de propriété

Visiteur des différentes classes filles de Property. Destinée à être héritée et concrétisée pour par exemple sauvegarder, charger ou afficher une propriété.
**/
class PropertyVisitor
{
public:
    virtual ~PropertyVisitor() = default;

56
public:
57
58
59
60
    virtual void visit(StringProperty& str) = 0;
    virtual void visit(IntegerProperty& str) = 0;
    virtual void visit(CoordinateProperty& coord) = 0;
    virtual void visit(PropertyList& str) = 0;
61
    virtual void visit(HelpProperty& str) = 0;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
};

/**
\class Property
\brief Représente une propriété

Une Property est un attribut membre d'une classe, pouvant être facilement visité par des PropertyVisitor afin de permettre facilement par exemple un affichage à l'aide de widgets des propriétés d'un objet, et facilitant ainsi le paramètrage par l'utilisateur d'un tel objet.
**/
class Property
{
    friend class HasUserProperties;

public:
    //! \brief Constructeur virtuel par défaut.
    virtual ~Property() = default;

    //! \brief Accepte un visiteur. Cette fonction est automatiquement redéfinie correctement en héritant de PropertyImpl<Classe>?
    virtual void accept(PropertyVisitor& v) = 0;

81
82
83
84
85
86
87
    //! \brief Retourne le nom identifiant propriété.
    std::string identifier() const
    { return m_identifier; }

    //! \brief Retourne le nom d'affichage de la propriété, qui est purement cosmétique.
    std::string display_name() const
    { return m_display_name; }
88
89

private:
90
91
    std::string m_identifier;
    std::string m_display_name;
92
93
94
95
};

/**
\class PropertyImpl
96
\brief Classe utilitaire pour les accepteur de PropertyVisitor.
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Classe utilitaire permettant de définir automatiquement 'accept' parmi les classes implémentant un type de Property.
 */
template <typename Child>
class PropertyImpl : public Property
{
public:
    /**
     \internal
     \brief Accepte le vitieur passé en argument afin de faire fonctionner le double-dispatch.
    **/
    virtual void accept(PropertyVisitor& v)
    {
        v.visit(static_cast<Child&>(*this));
    }
    //! \endinternal
};

/**
\class HasUserProperties
116
\brief Représente une classe possédant des Property.
117
118
119
120
121
122
123
124
125
126
127
128
Permet à une classe héritant de HasUSerProperties d'avoir des objets Property fonctionnels.
 **/
class HasUserProperties
{
public:
    //! \brief Retourne l'ensemble des Property associées à l'objet.
    const std::vector<std::unique_ptr<Property>>& get_properties() const
    {
        return m_properties;
    }

protected:
129
    //! \brief Appelée internalement par les macros DEFINE_CONFIGURABLE_*.
130
    template <typename PropertyType, typename... Args>
131
    PropertyType& register_property(const std::string& prop_name, const std::string& display_name, Args&&... args)
132
133
    {
        m_properties.emplace_back(std::make_unique<PropertyType>(std::forward<Args>(args)...));
134
135
        m_properties.back()->m_identifier = prop_name;
        m_properties.back()->m_display_name = display_name;
136
137
138
139
140
141
142
143
144
145
        return static_cast<PropertyType&>(*m_properties.back());
    }

private:
    std::vector<std::unique_ptr<Property>> m_properties;
};

/**
\class StringProperty

146
\brief Représente une propriété textuelle.
147
148
149
150
151
152
153
**/
struct StringProperty : public PropertyImpl<StringProperty>
{
public:
    //! \brief Constructeur par défaut.
    StringProperty() = default;
    /** \brief Constructeur avec valeur initiale.
Yann Boucher's avatar
Yann Boucher committed
154
        \param initval Valeur initiale.
155
156
157
158
159
160
    **/
    StringProperty(const std::string& initval)
        : str(initval)
    {}

public:
161
    //! \brief La chaîne de caractères.
162
163
164
    std::string str;
};

165
166
167
168
169
/**
\class HelpProperty

\brief Représente un bouton permettant l'affichage d'un manuel.
**/
170
struct HelpProperty : public PropertyImpl<HelpProperty>
171
172
173
174
175
{
public:
    /** \brief Constructeur avec valeur initiale.
        \param initval Valeur initiale.
    **/
176
    HelpProperty(const std::string& initval = "")
177
178
179
180
181
182
183
184
        : str(initval)
    {}

public:
    //! \brief La chaîne de caractères contenant l'aide.
    std::string str;
};

185
186
187
/**
\class IntegerProperty

188
\brief Représente une propriété entière bornée.
189
190
191
192
193
194
**/
struct IntegerProperty : public PropertyImpl<IntegerProperty>
{
public:
    //! \brief Construit un objet IntegerProperty avec une valeur min et une valeur max, non borné par défaut.
    IntegerProperty(int min_value = std::numeric_limits<int>::min(), int max_value = std::numeric_limits<int>::max())
195
        : val(min_value), prop_min(min_value), prop_max(max_value)
196
197
198
    {}

public:
199
    //! \brief La valeur de la propriété.
200
    int val = 0;
201
    //! \brief Les valeurs minimales et maximales de la propriété.
202
203
204
205
206
207
    const int prop_min, prop_max;
};

/**
\class CoordinateProperty

208
\brief Représente une coordonnée relative 2D.
209
210
211
212
**/
struct CoordinateProperty : public PropertyImpl<CoordinateProperty>
{
public:
213
214
215
216
    CoordinateProperty() = default;
    CoordinateProperty(Coord arg) : c(arg)
    {}

217
    //! \brief La valeur de la coordonnée.
218
    Coord c;
219
220
221
222
223
};

/**
\class PropertyList

224
\brief Représente une liste de propriétés.
225
226
227
228
**/
struct PropertyList : public PropertyImpl<PropertyList>
{
public:
229
230
231
    //! \brief Appelée internalement par la macro de création de liste, les arguments après is_dynamic sont accessibles depuis la macro, en arguments variables.
    //! \param in_min_size La taille minimale de la liste.
    //! \param in_max_size La taille maximale de la liste.
232
233
234
235
236
237
238
239
    PropertyList(std::unique_ptr<Property>(*create_function)(void), bool is_dynamic = false, int in_min_size = 0, int in_max_size = std::numeric_limits<int>::max())
        : dynamic(is_dynamic), min_size(in_min_size), max_size(in_max_size), m_create_function(create_function)
    {
        assert(m_create_function);
        for (int i = 0; i < min_size; ++i)
            push_back();
    }

240
241
    //! \brief Retourne true si la liste est vide, false sinon.
    //! \returns true si la liste est vide, false sinon.
242
243
    bool empty() const
    { return contents.empty(); }
244
    //! \brief Vide la liste.
245
246
    void clear()
    { contents.clear(); }
247
248
    //! \brief Retourne la taille actuelle de la liste.
    //! \returns La taille de la liste.
249
250
    size_t size() const
    { return contents.size(); }
251
252
    //! \brief Insère un élément dans la liste.
    //! \returns Une référence vers la propriété insérée.
253
254
255
256
257
258
    Property& push_back()
    {
        if ((int)size() < max_size)
            contents.emplace_back(m_create_function());
        return back();
    }
259
    //! \brief Enlève l'élément le plus à droite de la liste.
260
261
262
263
264
    void pop_back()
    {
        if (dynamic && !contents.empty() && (int)size() > min_size)
            contents.pop_back();
    }
265
266
    //! \brief Récupère l'élément le plus à droite de la liste.
    //! \returns Une référence vers cet élément.
267
268
    Property& back()
    { return *contents.back(); }
269
270
271
272
    //! \brief Récupère l'élément à un indice donné.
    //! \param i L'indice.
    //! \returns Une référence vers cet élément.
    //! \exception std::out_of_range si l'indice est invalide.
273
274
275
276
277
278
279
280
    Property& at(size_t i)
    {
        if (i < size())
            return *contents[i];
        else
            throw std::out_of_range("Invalid index value");
    }

281
282
283
284
285
286
287
288
289
290
291
292
    //! \brief Charge un voisinage dans la PropertyList
    void load_from_neighborhood(const Neighborhood& n)
    {
        clear();
        for (unsigned i = 0; i < n.size(); ++i)
        {
            Property& prop = push_back();
            CoordinateProperty& coord_p = static_cast<CoordinateProperty&>(prop);
            coord_p = n.neighbor_at_index(i).first;
        }
    }

293
294
295
296
297
298
299
300
301
302
303
304
305
306
    //! \brief Retourne les coordonées comme un Neighborhood, les cellules voisines étant d'état 1
    Neighborhood to_neighborhood() const
    {
        Neighborhood n;
        for (const auto& ptr : contents)
        {
            Property& prop = *ptr;
            CoordinateProperty& coord_p = static_cast<CoordinateProperty&>(prop);
            n.addNeighbor(coord_p.c, 1);
        }

        return n;
    }

307
public:
308
    //! \brief Vrai si la liste est extensible, faux si elle est fixe.
309
    const bool dynamic;
310
    //! \brief Taille minimale.
311
    const int min_size;
312
    //! \brief Taille maximale.
313
    const int max_size;
314
    //! \brief Le vector contenant les éléments de la liste.
315
316
317
318
319
320
321
322
323
324
325
    std::vector<std::unique_ptr<Property>> contents;
private:
    std::unique_ptr<Property>(*m_create_function)(void);
};

/** Permet de déclarer une variable propriété 'identifier' de type 'prop_type', avec comme nom affichable 'name'.
 \param prop_type Type de la propriété.
 \param identifier Nom de la variable dans le code.
 \param name Nom affichable de la propriété.
 \param ... Arguments supplémentaires à passer au constructeur de 'prop_type'
**/
326
327
#define DEFINE_CONFIGURABLE_PROPERTY(prop_type, identifier, display_name, ...) \
prop_type& identifier = register_property<prop_type>(#identifier, display_name, ##__VA_ARGS__)
328
329
330
331
332
333
334

/** Permet de déclarer une liste fixée de propriétés 'identifier' de type 'prop_type', avec comme nom affichable 'name'.
 \param prop_type Type des propriétés.
 \param identifier Nom de la variable dans le code.
 \param name Nom affichable de la liste de propriétés.
 \param ... Arguments supplémentaires à passer au constructeur de 'prop_type' lors de la créatin d'une propriété dans la liste
**/
335
336
#define DEFINE_CONFIGURABLE_LIST(prop_type, identifier, display_name, ...) \
PropertyList& identifier = register_property<PropertyList>(#identifier, display_name, []()->std::unique_ptr<Property>{ return std::make_unique<prop_type>(__VA_ARGS__);  false, ##__VA_ARGS__ })
337
338
339
340
341
342
343
/** Permet de déclarer une liste dynamique de propriétés 'identifier' de type 'prop_type', avec comme nom affichable 'name'.
 \param prop_type Type des propriétés.
 \param identifier Nom de la variable dans le code.
 \param name Nom affichable de la liste de propriétés.
 \param inner_args Arguments supplémentaires à passer au constructeur de 'prop_type' lors de la créatin d'une propriété dans la liste
 \param ... Arguments supplémentaires à passer au constructeur de PropertyList.
**/
344
345
#define DEFINE_CONFIGURABLE_DYNLIST(prop_type, identifier, display_name, inner_args, ...) \
PropertyList& identifier = register_property<PropertyList>(#identifier, display_name, []()->std::unique_ptr<Property>{ return std::make_unique<prop_type> inner_args; }, true, ##__VA_ARGS__ )
346
347

#endif // PROPERTY_HPP