1 | /* |
2 | * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> |
3 | * |
4 | * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
5 | */ |
6 | |
7 | #include "ItemBuilder.h" |
8 | |
9 | class ItemIncubator : public QQmlIncubator |
10 | { |
11 | public: |
12 | ItemIncubator(QQmlComponent *component, QQmlContext *context) |
13 | { |
14 | m_component = component; |
15 | m_context = context; |
16 | } |
17 | |
18 | void setStateCallback(std::function<void(QQuickItem*)> callback) |
19 | { |
20 | m_stateCallback = callback; |
21 | } |
22 | |
23 | void setCompletedCallback(std::function<void(ItemIncubator*)> callback) |
24 | { |
25 | m_completedCallback = callback; |
26 | } |
27 | |
28 | void create() |
29 | { |
30 | m_component->create(*this, context: m_context); |
31 | } |
32 | |
33 | bool isFinished() |
34 | { |
35 | return m_finished; |
36 | } |
37 | |
38 | private: |
39 | void setInitialState(QObject *object) override |
40 | { |
41 | auto item = qobject_cast<QQuickItem*>(o: object); |
42 | if (item) { |
43 | m_stateCallback(item); |
44 | } |
45 | } |
46 | |
47 | void statusChanged(QQmlIncubator::Status status) override |
48 | { |
49 | if (status == QQmlIncubator::Error) { |
50 | qWarning() << "Could not create delegate in ItemBuilder" ; |
51 | const auto e = errors(); |
52 | for (const auto &error : e) { |
53 | qWarning() << error; |
54 | } |
55 | m_finished = true; |
56 | } |
57 | |
58 | if (status == QQmlIncubator::Ready) { |
59 | m_completedCallback(this); |
60 | m_finished = true; |
61 | } |
62 | } |
63 | |
64 | QQmlComponent *m_component; |
65 | QQmlContext *m_context; |
66 | std::function<void(QQuickItem*)> m_stateCallback; |
67 | std::function<void(ItemIncubator*)> m_completedCallback; |
68 | bool m_finished = false; |
69 | }; |
70 | |
71 | ItemBuilder::ItemBuilder(QObject *parent) |
72 | : QObject(parent) |
73 | { |
74 | } |
75 | |
76 | ItemBuilder::~ItemBuilder() |
77 | { |
78 | clear(); |
79 | } |
80 | |
81 | QQmlComponent *ItemBuilder::component() const |
82 | { |
83 | return m_component; |
84 | } |
85 | |
86 | void ItemBuilder::setComponent(QQmlComponent *newComponent) |
87 | { |
88 | if (newComponent == m_component) { |
89 | return; |
90 | } |
91 | |
92 | m_component = newComponent; |
93 | clear(); |
94 | } |
95 | |
96 | QQmlContext *ItemBuilder::context() const |
97 | { |
98 | return m_context; |
99 | } |
100 | |
101 | void ItemBuilder::setContext(QQmlContext *newContext) |
102 | { |
103 | if (newContext == m_context) { |
104 | return; |
105 | } |
106 | |
107 | m_context = newContext; |
108 | clear(); |
109 | } |
110 | |
111 | |
112 | int ItemBuilder::count() const |
113 | { |
114 | return m_count; |
115 | } |
116 | |
117 | void ItemBuilder::setCount(int newCount) |
118 | { |
119 | if (newCount == m_count) { |
120 | return; |
121 | } |
122 | |
123 | m_count = newCount; |
124 | clear(); |
125 | } |
126 | |
127 | QQmlIncubator::IncubationMode ItemBuilder::incubationMode() const |
128 | { |
129 | return m_incubationMode; |
130 | } |
131 | |
132 | void ItemBuilder::setIncubationMode(QQmlIncubator::IncubationMode newIncubationMode) |
133 | { |
134 | if (newIncubationMode == m_incubationMode) { |
135 | return; |
136 | } |
137 | |
138 | m_incubationMode = newIncubationMode; |
139 | } |
140 | |
141 | QVariantMap ItemBuilder::initialProperties() const |
142 | { |
143 | return m_initialProperties; |
144 | } |
145 | |
146 | void ItemBuilder::setInitialProperties(const QVariantMap & newInitialProperties) |
147 | { |
148 | if (newInitialProperties == m_initialProperties) { |
149 | return; |
150 | } |
151 | |
152 | m_initialProperties = newInitialProperties; |
153 | } |
154 | |
155 | void ItemBuilder::build(QQuickItem *parent) |
156 | { |
157 | if ((int(m_items.size()) == m_count && m_incubators.empty()) || !m_incubators.empty() || !m_component) { |
158 | return; |
159 | } |
160 | |
161 | m_incubators.reserve(n: m_count); |
162 | std::fill_n(first: std::back_inserter(x&: m_items), n: m_count, value: std::shared_ptr<QQuickItem>()); |
163 | |
164 | for (int i = 0; i < m_count; ++i) { |
165 | auto context = m_context ? m_context : qmlContext(m_component); |
166 | auto incubator = std::make_unique<ItemIncubator>(args&: m_component, args&: context); |
167 | |
168 | incubator->setStateCallback([this, parent, i](QQuickItem *item) { |
169 | item->setParentItem(parent); |
170 | |
171 | for (auto itr = m_initialProperties.keyValueBegin(); itr != m_initialProperties.keyValueEnd(); ++itr) { |
172 | item->setProperty(name: (*itr).first.toUtf8().data(), value: (*itr).second); |
173 | } |
174 | |
175 | Q_EMIT beginCreate(index: i, item); |
176 | }); |
177 | |
178 | incubator->setCompletedCallback([this, i](ItemIncubator *incubator) { |
179 | auto item = std::shared_ptr<QQuickItem>(qobject_cast<QQuickItem*>(o: incubator->object())); |
180 | m_items[i] = item; |
181 | |
182 | Q_EMIT endCreate(index: i, item: item.get()); |
183 | |
184 | m_completed++; |
185 | if (m_completed == m_count) { |
186 | QMetaObject::invokeMethod(object: this, function: [this]() { |
187 | m_incubators.clear(); |
188 | }, type: Qt::QueuedConnection); |
189 | Q_EMIT finished(); |
190 | } |
191 | }); |
192 | |
193 | incubator->create(); |
194 | |
195 | m_incubators.push_back(x: std::move(incubator)); |
196 | } |
197 | } |
198 | |
199 | bool ItemBuilder::isFinished() const |
200 | { |
201 | return m_completed == m_count; |
202 | } |
203 | |
204 | std::vector<std::shared_ptr<QQuickItem>> ItemBuilder::items() const |
205 | { |
206 | return m_items; |
207 | } |
208 | |
209 | void ItemBuilder::clear() |
210 | { |
211 | m_items.clear(); |
212 | |
213 | if (m_incubators.size() > 0) { |
214 | for (auto &incubator : m_incubators) { |
215 | incubator->clear(); |
216 | } |
217 | } |
218 | m_incubators.clear(); |
219 | |
220 | m_completed = 0; |
221 | } |
222 | |
223 | #include "moc_ItemBuilder.cpp" |
224 | |