1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "declarativetheme_p.h" |
5 | |
6 | QT_BEGIN_NAMESPACE |
7 | |
8 | DeclarativeTheme3D::DeclarativeTheme3D(QObject *parent) |
9 | : Q3DTheme(parent), |
10 | m_colors(QList<DeclarativeColor *>()), |
11 | m_gradients(QList<ColorGradient *>()), |
12 | m_singleHLGradient(0), |
13 | m_multiHLGradient(0), |
14 | m_dummyGradients(false), |
15 | m_dummyColors(false) |
16 | { |
17 | connect(sender: this, signal: &Q3DTheme::typeChanged, context: this, slot: &DeclarativeTheme3D::handleTypeChange); |
18 | } |
19 | |
20 | DeclarativeTheme3D::~DeclarativeTheme3D() |
21 | { |
22 | } |
23 | |
24 | QQmlListProperty<QObject> DeclarativeTheme3D::themeChildren() |
25 | { |
26 | return QQmlListProperty<QObject>(this, this, &DeclarativeTheme3D::appendThemeChildren, |
27 | 0, 0, 0); |
28 | } |
29 | |
30 | void DeclarativeTheme3D::appendThemeChildren(QQmlListProperty<QObject> *list, QObject *element) |
31 | { |
32 | Q_UNUSED(list); |
33 | Q_UNUSED(element); |
34 | // Nothing to do, themeChildren is there only to enable scoping gradient items in Theme3D item. |
35 | } |
36 | |
37 | void DeclarativeTheme3D::handleTypeChange(Theme themeType) |
38 | { |
39 | Q_UNUSED(themeType); |
40 | |
41 | // Theme changed, disconnect base color/gradient connections |
42 | if (!m_colors.isEmpty()) { |
43 | foreach (DeclarativeColor *item, m_colors) |
44 | disconnect(sender: item, signal: 0, receiver: this, member: 0); |
45 | m_colors.clear(); |
46 | } |
47 | if (!m_gradients.isEmpty()) { |
48 | foreach (ColorGradient *item, m_gradients) |
49 | disconnect(sender: item, signal: 0, receiver: this, member: 0); |
50 | m_gradients.clear(); |
51 | } |
52 | } |
53 | |
54 | void DeclarativeTheme3D::handleBaseColorUpdate() |
55 | { |
56 | int colorCount = m_colors.size(); |
57 | int changed = 0; |
58 | // Check which one changed |
59 | DeclarativeColor *color = qobject_cast<DeclarativeColor *>(object: QObject::sender()); |
60 | for (int i = 0; i < colorCount; i++) { |
61 | if (color == m_colors.at(i)) { |
62 | changed = i; |
63 | break; |
64 | } |
65 | } |
66 | // Update the changed one from the list |
67 | QList<QColor> list = Q3DTheme::baseColors(); |
68 | list[changed] = m_colors.at(i: changed)->color(); |
69 | // Set the changed list |
70 | Q3DTheme::setBaseColors(list); |
71 | } |
72 | |
73 | void DeclarativeTheme3D::handleBaseGradientUpdate() |
74 | { |
75 | int gradientCount = m_gradients.size(); |
76 | int changed = 0; |
77 | // Check which one changed |
78 | ColorGradient *gradient = qobject_cast<ColorGradient *>(object: QObject::sender()); |
79 | for (int i = 0; i < gradientCount; i++) { |
80 | if (gradient == m_gradients.at(i)) { |
81 | changed = i; |
82 | break; |
83 | } |
84 | } |
85 | // Update the changed one from the list |
86 | QList<QLinearGradient> list = Q3DTheme::baseGradients(); |
87 | list[changed] = convertGradient(gradient); |
88 | // Set the changed list |
89 | Q3DTheme::setBaseGradients(list); |
90 | } |
91 | |
92 | void DeclarativeTheme3D::handleSingleHLGradientUpdate() |
93 | { |
94 | if (m_singleHLGradient) |
95 | setThemeGradient(gradient: m_singleHLGradient, type: GradientTypeSingleHL); |
96 | } |
97 | |
98 | void DeclarativeTheme3D::handleMultiHLGradientUpdate() |
99 | { |
100 | if (m_multiHLGradient) |
101 | setThemeGradient(gradient: m_multiHLGradient, type: GradientTypeMultiHL); |
102 | } |
103 | |
104 | void DeclarativeTheme3D::setSingleHighlightGradient(ColorGradient *gradient) |
105 | { |
106 | // connect new / disconnect old |
107 | if (gradient != m_singleHLGradient) { |
108 | if (m_singleHLGradient) |
109 | QObject::disconnect(sender: m_singleHLGradient, signal: 0, receiver: this, member: 0); |
110 | |
111 | m_singleHLGradient = gradient; |
112 | |
113 | if (m_singleHLGradient) { |
114 | QObject::connect(sender: m_singleHLGradient, signal: &ColorGradient::updated, context: this, |
115 | slot: &DeclarativeTheme3D::handleSingleHLGradientUpdate); |
116 | } |
117 | |
118 | emit singleHighlightGradientChanged(gradient: m_singleHLGradient); |
119 | } |
120 | |
121 | if (m_singleHLGradient) |
122 | setThemeGradient(gradient: m_singleHLGradient, type: GradientTypeSingleHL); |
123 | } |
124 | |
125 | ColorGradient *DeclarativeTheme3D::singleHighlightGradient() const |
126 | { |
127 | return m_singleHLGradient; |
128 | } |
129 | |
130 | void DeclarativeTheme3D::setMultiHighlightGradient(ColorGradient *gradient) |
131 | { |
132 | // connect new / disconnect old |
133 | if (gradient != m_multiHLGradient) { |
134 | if (m_multiHLGradient) |
135 | QObject::disconnect(sender: m_multiHLGradient, signal: 0, receiver: this, member: 0); |
136 | |
137 | m_multiHLGradient = gradient; |
138 | |
139 | if (m_multiHLGradient) { |
140 | QObject::connect(sender: m_multiHLGradient, signal: &ColorGradient::updated, context: this, |
141 | slot: &DeclarativeTheme3D::handleMultiHLGradientUpdate); |
142 | } |
143 | |
144 | emit multiHighlightGradientChanged(gradient: m_multiHLGradient); |
145 | } |
146 | |
147 | if (m_multiHLGradient) |
148 | setThemeGradient(gradient: m_multiHLGradient, type: GradientTypeMultiHL); |
149 | } |
150 | |
151 | ColorGradient *DeclarativeTheme3D::multiHighlightGradient() const |
152 | { |
153 | return m_multiHLGradient; |
154 | } |
155 | |
156 | void DeclarativeTheme3D::classBegin() |
157 | { |
158 | // Turn off predefined type forcing for the duration of initial class construction |
159 | // so that predefined type customization can be done. |
160 | d_func()->setForcePredefinedType(false); |
161 | } |
162 | |
163 | void DeclarativeTheme3D::componentComplete() |
164 | { |
165 | d_func()->setForcePredefinedType(true); |
166 | } |
167 | |
168 | |
169 | void DeclarativeTheme3D::setThemeGradient(ColorGradient *gradient, GradientType type) |
170 | { |
171 | QLinearGradient newGradient = convertGradient(gradient); |
172 | |
173 | switch (type) { |
174 | case GradientTypeSingleHL: |
175 | Q3DTheme::setSingleHighlightGradient(newGradient); |
176 | break; |
177 | case GradientTypeMultiHL: |
178 | Q3DTheme::setMultiHighlightGradient(newGradient); |
179 | break; |
180 | default: |
181 | qWarning(msg: "Incorrect usage. Type may be GradientTypeSingleHL or GradientTypeMultiHL." ); |
182 | break; |
183 | } |
184 | } |
185 | |
186 | QLinearGradient DeclarativeTheme3D::convertGradient(ColorGradient *gradient) |
187 | { |
188 | QLinearGradient newGradient; |
189 | QGradientStops stops; |
190 | QList<ColorGradientStop *> qmlstops = gradient->m_stops; |
191 | |
192 | // Get sorted gradient stops |
193 | for (int i = 0; i < qmlstops.size(); i++) { |
194 | int j = 0; |
195 | while (j < stops.size() && stops.at(i: j).first < qmlstops[i]->position()) |
196 | j++; |
197 | stops.insert(i: j, t: QGradientStop(qmlstops.at(i)->position(), qmlstops.at(i)->color())); |
198 | } |
199 | |
200 | newGradient.setStops(stops); |
201 | |
202 | return newGradient; |
203 | } |
204 | |
205 | ColorGradient *DeclarativeTheme3D::convertGradient(const QLinearGradient &gradient) |
206 | { |
207 | ColorGradient *newGradient = new ColorGradient(this); |
208 | QGradientStops stops = gradient.stops(); |
209 | ColorGradientStop *qmlstop; |
210 | |
211 | // Convert stops |
212 | for (int i = 0; i < stops.size(); i++) { |
213 | qmlstop = new ColorGradientStop(newGradient); |
214 | qmlstop->setColor(stops.at(i).second); |
215 | qmlstop->setPosition(stops.at(i).first); |
216 | newGradient->m_stops.append(t: qmlstop); |
217 | } |
218 | |
219 | return newGradient; |
220 | } |
221 | |
222 | void DeclarativeTheme3D::addColor(DeclarativeColor *color) |
223 | { |
224 | if (!color) { |
225 | qWarning(msg: "Color is invalid, use ThemeColor" ); |
226 | return; |
227 | } |
228 | clearDummyColors(); |
229 | m_colors.append(t: color); |
230 | connect(sender: color, signal: &DeclarativeColor::colorChanged, |
231 | context: this, slot: &DeclarativeTheme3D::handleBaseColorUpdate); |
232 | QList<QColor> list = Q3DTheme::baseColors(); |
233 | list.append(t: color->color()); |
234 | Q3DTheme::setBaseColors(list); |
235 | } |
236 | |
237 | QList<DeclarativeColor *> DeclarativeTheme3D::colorList() |
238 | { |
239 | if (m_colors.isEmpty()) { |
240 | // Create dummy ThemeColors from theme's colors |
241 | m_dummyColors = true; |
242 | QList<QColor> list = Q3DTheme::baseColors(); |
243 | foreach (QColor item, list) { |
244 | DeclarativeColor *color = new DeclarativeColor(this); |
245 | color->setColor(item); |
246 | m_colors.append(t: color); |
247 | connect(sender: color, signal: &DeclarativeColor::colorChanged, |
248 | context: this, slot: &DeclarativeTheme3D::handleBaseColorUpdate); |
249 | } |
250 | } |
251 | return m_colors; |
252 | } |
253 | |
254 | void DeclarativeTheme3D::clearColors() |
255 | { |
256 | clearDummyColors(); |
257 | foreach (DeclarativeColor *item, m_colors) |
258 | disconnect(sender: item, signal: 0, receiver: this, member: 0); |
259 | m_colors.clear(); |
260 | Q3DTheme::setBaseColors(QList<QColor>()); |
261 | } |
262 | |
263 | void DeclarativeTheme3D::clearDummyColors() |
264 | { |
265 | if (m_dummyColors) { |
266 | foreach (DeclarativeColor *item, m_colors) |
267 | delete item; |
268 | m_colors.clear(); |
269 | m_dummyColors = false; |
270 | } |
271 | } |
272 | |
273 | void DeclarativeTheme3D::addGradient(ColorGradient *gradient) |
274 | { |
275 | if (!gradient) { |
276 | qWarning(msg: "Gradient is invalid, use ColorGradient" ); |
277 | return; |
278 | } |
279 | clearDummyGradients(); |
280 | m_gradients.append(t: gradient); |
281 | connect(sender: gradient, signal: &ColorGradient::updated, |
282 | context: this, slot: &DeclarativeTheme3D::handleBaseGradientUpdate); |
283 | QList<QLinearGradient> list = Q3DTheme::baseGradients(); |
284 | list.append(t: convertGradient(gradient)); |
285 | Q3DTheme::setBaseGradients(list); |
286 | } |
287 | |
288 | QList<ColorGradient *> DeclarativeTheme3D::gradientList() |
289 | { |
290 | if (m_gradients.isEmpty()) { |
291 | // Create dummy ColorGradients from theme's gradients |
292 | m_dummyGradients = true; |
293 | QList<QLinearGradient> list = Q3DTheme::baseGradients(); |
294 | foreach (QLinearGradient item, list) { |
295 | ColorGradient *gradient = convertGradient(gradient: item); |
296 | m_gradients.append(t: gradient); |
297 | connect(sender: gradient, signal: &ColorGradient::updated, |
298 | context: this, slot: &DeclarativeTheme3D::handleBaseGradientUpdate); |
299 | } |
300 | } |
301 | |
302 | return m_gradients; |
303 | } |
304 | |
305 | void DeclarativeTheme3D::clearGradients() |
306 | { |
307 | clearDummyGradients(); |
308 | foreach (ColorGradient *item, m_gradients) |
309 | disconnect(sender: item, signal: 0, receiver: this, member: 0); |
310 | m_gradients.clear(); |
311 | Q3DTheme::setBaseGradients(QList<QLinearGradient>()); |
312 | } |
313 | |
314 | void DeclarativeTheme3D::clearDummyGradients() |
315 | { |
316 | if (m_dummyGradients) { |
317 | foreach (ColorGradient *item, m_gradients) |
318 | delete item; |
319 | m_gradients.clear(); |
320 | m_dummyGradients = false; |
321 | } |
322 | } |
323 | |
324 | QQmlListProperty<DeclarativeColor> DeclarativeTheme3D::baseColors() |
325 | { |
326 | return QQmlListProperty<DeclarativeColor>(this, this, |
327 | &DeclarativeTheme3D::appendBaseColorsFunc, |
328 | &DeclarativeTheme3D::countBaseColorsFunc, |
329 | &DeclarativeTheme3D::atBaseColorsFunc, |
330 | &DeclarativeTheme3D::clearBaseColorsFunc); |
331 | } |
332 | |
333 | void DeclarativeTheme3D::appendBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list, |
334 | DeclarativeColor *color) |
335 | { |
336 | reinterpret_cast<DeclarativeTheme3D *>(list->data)->addColor(color); |
337 | } |
338 | |
339 | qsizetype DeclarativeTheme3D::countBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list) |
340 | { |
341 | return reinterpret_cast<DeclarativeTheme3D *>(list->data)->colorList().size(); |
342 | } |
343 | |
344 | DeclarativeColor *DeclarativeTheme3D::atBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list, |
345 | qsizetype index) |
346 | { |
347 | return reinterpret_cast<DeclarativeTheme3D *>(list->data)->colorList().at(i: index); |
348 | } |
349 | |
350 | void DeclarativeTheme3D::clearBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list) |
351 | { |
352 | reinterpret_cast<DeclarativeTheme3D *>(list->data)->clearColors(); |
353 | } |
354 | |
355 | QQmlListProperty<ColorGradient> DeclarativeTheme3D::baseGradients() |
356 | { |
357 | return QQmlListProperty<ColorGradient>(this, this, |
358 | &DeclarativeTheme3D::appendBaseGradientsFunc, |
359 | &DeclarativeTheme3D::countBaseGradientsFunc, |
360 | &DeclarativeTheme3D::atBaseGradientsFunc, |
361 | &DeclarativeTheme3D::clearBaseGradientsFunc); |
362 | } |
363 | |
364 | void DeclarativeTheme3D::appendBaseGradientsFunc(QQmlListProperty<ColorGradient> *list, |
365 | ColorGradient *gradient) |
366 | { |
367 | reinterpret_cast<DeclarativeTheme3D *>(list->data)->addGradient(gradient); |
368 | } |
369 | |
370 | qsizetype DeclarativeTheme3D::countBaseGradientsFunc(QQmlListProperty<ColorGradient> *list) |
371 | { |
372 | return reinterpret_cast<DeclarativeTheme3D *>(list->data)->gradientList().size(); |
373 | } |
374 | |
375 | ColorGradient *DeclarativeTheme3D::atBaseGradientsFunc(QQmlListProperty<ColorGradient> *list, |
376 | qsizetype index) |
377 | { |
378 | return reinterpret_cast<DeclarativeTheme3D *>(list->data)->gradientList().at(i: index); |
379 | } |
380 | |
381 | void DeclarativeTheme3D::clearBaseGradientsFunc(QQmlListProperty<ColorGradient> *list) |
382 | { |
383 | reinterpret_cast<DeclarativeTheme3D *>(list->data)->clearGradients(); |
384 | } |
385 | |
386 | QT_END_NAMESPACE |
387 | |