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
6QT_BEGIN_NAMESPACE
7
8DeclarativeTheme3D::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
20DeclarativeTheme3D::~DeclarativeTheme3D()
21{
22}
23
24QQmlListProperty<QObject> DeclarativeTheme3D::themeChildren()
25{
26 return QQmlListProperty<QObject>(this, this, &DeclarativeTheme3D::appendThemeChildren,
27 0, 0, 0);
28}
29
30void 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
37void 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
54void 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
73void 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
92void DeclarativeTheme3D::handleSingleHLGradientUpdate()
93{
94 if (m_singleHLGradient)
95 setThemeGradient(gradient: m_singleHLGradient, type: GradientTypeSingleHL);
96}
97
98void DeclarativeTheme3D::handleMultiHLGradientUpdate()
99{
100 if (m_multiHLGradient)
101 setThemeGradient(gradient: m_multiHLGradient, type: GradientTypeMultiHL);
102}
103
104void 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
125ColorGradient *DeclarativeTheme3D::singleHighlightGradient() const
126{
127 return m_singleHLGradient;
128}
129
130void 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
151ColorGradient *DeclarativeTheme3D::multiHighlightGradient() const
152{
153 return m_multiHLGradient;
154}
155
156void 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
163void DeclarativeTheme3D::componentComplete()
164{
165 d_func()->setForcePredefinedType(true);
166}
167
168
169void 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
186QLinearGradient 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
205ColorGradient *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
222void 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
237QList<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
254void 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
263void 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
273void 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
288QList<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
305void 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
314void 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
324QQmlListProperty<DeclarativeColor> DeclarativeTheme3D::baseColors()
325{
326 return QQmlListProperty<DeclarativeColor>(this, this,
327 &DeclarativeTheme3D::appendBaseColorsFunc,
328 &DeclarativeTheme3D::countBaseColorsFunc,
329 &DeclarativeTheme3D::atBaseColorsFunc,
330 &DeclarativeTheme3D::clearBaseColorsFunc);
331}
332
333void DeclarativeTheme3D::appendBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list,
334 DeclarativeColor *color)
335{
336 reinterpret_cast<DeclarativeTheme3D *>(list->data)->addColor(color);
337}
338
339qsizetype DeclarativeTheme3D::countBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list)
340{
341 return reinterpret_cast<DeclarativeTheme3D *>(list->data)->colorList().size();
342}
343
344DeclarativeColor *DeclarativeTheme3D::atBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list,
345 qsizetype index)
346{
347 return reinterpret_cast<DeclarativeTheme3D *>(list->data)->colorList().at(i: index);
348}
349
350void DeclarativeTheme3D::clearBaseColorsFunc(QQmlListProperty<DeclarativeColor> *list)
351{
352 reinterpret_cast<DeclarativeTheme3D *>(list->data)->clearColors();
353}
354
355QQmlListProperty<ColorGradient> DeclarativeTheme3D::baseGradients()
356{
357 return QQmlListProperty<ColorGradient>(this, this,
358 &DeclarativeTheme3D::appendBaseGradientsFunc,
359 &DeclarativeTheme3D::countBaseGradientsFunc,
360 &DeclarativeTheme3D::atBaseGradientsFunc,
361 &DeclarativeTheme3D::clearBaseGradientsFunc);
362}
363
364void DeclarativeTheme3D::appendBaseGradientsFunc(QQmlListProperty<ColorGradient> *list,
365 ColorGradient *gradient)
366{
367 reinterpret_cast<DeclarativeTheme3D *>(list->data)->addGradient(gradient);
368}
369
370qsizetype DeclarativeTheme3D::countBaseGradientsFunc(QQmlListProperty<ColorGradient> *list)
371{
372 return reinterpret_cast<DeclarativeTheme3D *>(list->data)->gradientList().size();
373}
374
375ColorGradient *DeclarativeTheme3D::atBaseGradientsFunc(QQmlListProperty<ColorGradient> *list,
376 qsizetype index)
377{
378 return reinterpret_cast<DeclarativeTheme3D *>(list->data)->gradientList().at(i: index);
379}
380
381void DeclarativeTheme3D::clearBaseGradientsFunc(QQmlListProperty<ColorGradient> *list)
382{
383 reinterpret_cast<DeclarativeTheme3D *>(list->data)->clearGradients();
384}
385
386QT_END_NAMESPACE
387

source code of qtgraphs/src/graphs/qml/declarativetheme.cpp