18 |
18 |
19 #pragma once |
19 #pragma once |
20 #include <QAbstractListModel> |
20 #include <QAbstractListModel> |
21 #include <memory> |
21 #include <memory> |
22 #include "main.h" |
22 #include "main.h" |
23 #include "header.h" |
23 #include "colors.h" |
24 #include "linetypes/object.h" |
|
25 #include "linetypes/metacommand.h" |
|
26 #include "gl/common.h" |
|
27 |
24 |
28 enum class HeaderProperty |
25 struct SubfileReference |
29 { |
26 { |
30 Name |
27 QString name; |
|
28 glm::mat4 transformation; |
|
29 bool inverted = false; |
31 }; |
30 }; |
|
31 |
|
32 template<typename T> |
|
33 struct Colored : T |
|
34 { |
|
35 ldraw::Color color; |
|
36 }; |
|
37 |
|
38 struct Comment |
|
39 { |
|
40 QString text; |
|
41 }; |
|
42 |
|
43 struct ParseError |
|
44 { |
|
45 QString code; |
|
46 }; |
|
47 |
|
48 struct Empty {}; |
|
49 |
|
50 using ModelElement = std::variant< |
|
51 Colored<SubfileReference>, |
|
52 Colored<LineSegment>, |
|
53 Colored<Triangle>, |
|
54 Colored<Quadrilateral>, |
|
55 Colored<ConditionalEdge>, |
|
56 Comment, |
|
57 Empty, |
|
58 ParseError>; |
|
59 |
|
60 QString modelElementToString(const ModelElement& element); |
|
61 struct ModelId |
|
62 { |
|
63 std::int32_t value; |
|
64 constexpr auto operator<=>(const ModelId& other) const = default; |
|
65 }; |
|
66 |
|
67 constexpr int qHash(ModelId id) |
|
68 { |
|
69 return qHash(id.value); |
|
70 } |
32 |
71 |
33 class Model : public QAbstractListModel |
72 class Model : public QAbstractListModel |
34 { |
73 { |
35 Q_OBJECT |
74 Q_OBJECT |
|
75 struct Entry { |
|
76 ModelElement data; |
|
77 ModelId id; |
|
78 }; |
|
79 std::vector<Entry> body; |
|
80 std::map<ModelId, int> positions; |
|
81 ModelId runningId = {1}; |
36 public: |
82 public: |
37 Model(QObject* parent = nullptr); |
83 Model(QObject* parent); |
38 Model(const Model&) = delete; |
84 virtual ~Model(); |
39 |
85 ModelId append(const ModelElement& value); |
40 int size() const; |
86 const ModelElement& at(int position) const; |
41 ldraw::id_t at(int index) const; |
87 ModelId idAt(int position) const; |
|
88 void assignAt(int position, const ModelElement& element); |
|
89 std::optional<int> find(ModelId id) const; |
|
90 void remove(int index); |
42 int rowCount(const QModelIndex&) const override; |
91 int rowCount(const QModelIndex&) const override; |
43 QVariant data(const QModelIndex& index, int role) const override; |
92 QVariant data(const QModelIndex& index, int role) const override; |
44 ldraw::Object* findObjectById(const ldraw::id_t id); |
93 const ModelElement& operator[](int index) const; |
45 const ldraw::Object* findObjectById(const ldraw::id_t id) const; |
94 int size() const; |
46 QModelIndex find(ldraw::id_t id) const; |
95 auto operator[](int index) { |
47 ldraw::id_t idAt(const QModelIndex& index) const; |
96 struct { |
48 template<typename R> |
97 Model& model; |
49 const R* get(ldraw::Id<R> id) const; |
98 int index; |
50 template<typename R> |
99 operator const ModelElement&() { |
51 struct Get2Result |
100 return model.at(index); |
52 { |
101 } |
53 QModelIndex index; |
102 auto& operator=(const ModelElement& newData) { |
54 const R* object; |
103 model.assignAt(index, newData); |
55 }; |
104 return *this; |
56 template<typename R> |
105 } |
57 Get2Result<R> get2(ldraw::Id<R> id) const; |
106 const auto* operator&() { |
58 ldraw::Object* operator[](int index); |
107 return &(this->operator const ModelElement&()); |
59 const ldraw::Object* operator[](int index) const; |
108 } |
60 using ModelObjectPointer = std::unique_ptr<ldraw::Object>; |
109 } result{*this, index}; |
61 template<typename T, typename... Args> |
110 return result; |
62 ldraw::Id<T> append(Args&&... args); |
111 } |
63 ldraw::id_t append(ModelObjectPointer&& object); |
|
64 template<typename T, typename... Args> |
|
65 ldraw::Id<T> insert(std::size_t position, Args&&... args); |
|
66 void remove(int position); |
|
67 void emitDataChangedSignal(int position); |
|
68 private: |
|
69 bool modified = false; |
|
70 std::vector<ModelObjectPointer> body; |
|
71 mutable std::map<ldraw::id_t, std::size_t> objectsById; |
|
72 mutable bool needObjectsByIdRebuild = false; |
|
73 }; |
112 }; |
74 |
113 |
75 void save(const Model& model, QIODevice *device); |
114 void save(const Model& model, QIODevice *device); |
76 |
115 void updateHeaderNameField(Model& model, const QString &name); |
77 /** |
|
78 * @brief Calls the specified function to all matching objects in the model |
|
79 * @tparam R Type of LDraw line type object to filter by |
|
80 * @param fn Function to call. |
|
81 */ |
|
82 template<typename R, typename Fn> |
|
83 void applyToModel(const Model& model, Fn&& f) |
|
84 { |
|
85 for (int i = 0; i < model.size(); i += 1) |
|
86 { |
|
87 const ldraw::Object* object = model[i]; |
|
88 const R* subobject = dynamic_cast<const R*>(object); |
|
89 if (subobject != nullptr) |
|
90 { |
|
91 f(subobject); |
|
92 } |
|
93 } |
|
94 } |
|
95 |
|
96 template<typename T, typename... Args> |
|
97 ldraw::Id<T> Model::append(Args&&... args) |
|
98 { |
|
99 const int position = static_cast<int>(this->body.size()); |
|
100 Q_EMIT beginInsertRows({}, position, position); |
|
101 this->body.push_back(std::make_unique<T>(args...)); |
|
102 ldraw::Object* pointer = this->body.back().get(); |
|
103 this->objectsById[pointer->id] = this->body.size() - 1; |
|
104 Q_EMIT endInsertRows(); |
|
105 return ldraw::Id<T>{pointer->id.value}; |
|
106 } |
|
107 |
|
108 template<typename T, typename... Args> |
|
109 ldraw::Id<T> Model::insert(const std::size_t position, Args&&... args) |
|
110 { |
|
111 Q_EMIT beginInsertRows({}, position, position); |
|
112 this->body.insert(std::begin(this->body) + position, std::make_unique<T>(args...)); |
|
113 ldraw::Object* pointer = this->body[position].get(); |
|
114 this->objectsById[pointer->id] = position; |
|
115 Q_EMIT endInsertRows(); |
|
116 return ldraw::Id<T>{pointer->id.value}; |
|
117 } |
|
118 |
|
119 template<typename R> |
|
120 const R* Model::get(ldraw::Id<R> id) const |
|
121 { |
|
122 return this->get2(id).object; |
|
123 } |
|
124 |
|
125 template<typename R> |
|
126 Model::Get2Result<R> Model::get2(const ldraw::Id<R> id) const |
|
127 { |
|
128 Get2Result<R> result; |
|
129 result.index = this->find(id); |
|
130 if (result.index.isValid()) |
|
131 { |
|
132 result.object = static_cast<const R*>((*this)[result.index.row()]); |
|
133 } |
|
134 else |
|
135 { |
|
136 result.object = nullptr; |
|
137 } |
|
138 return result; |
|
139 } |
|