1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickcolorinputs_p.h" |
5 | |
6 | #include <QtCore/QRegularExpression> |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | QQuickColorInputs::QQuickColorInputs() = default; |
11 | |
12 | QColor QQuickColorInputs::color() const |
13 | { |
14 | return QColor::fromHsvF(h: m_hsva.h, s: m_hsva.s, v: m_hsva.v, a: m_hsva.a); |
15 | } |
16 | |
17 | void QQuickColorInputs::setColor(const QColor &c) |
18 | { |
19 | if (color().rgba() == c.rgba()) |
20 | return; |
21 | |
22 | // If we get a QColor from an Hsv or Hsl color system, |
23 | // we want to get the raw values without the risk of QColor converting them, |
24 | // and possible deleting relevant information for achromatic cases. |
25 | if (c.spec() == QColor::Spec::Hsl) { |
26 | const auto sv = getSaturationAndValue(saturation: c.hslSaturationF(), lightness: c.lightnessF()); |
27 | m_hsva.h = qBound(min: .0, val: c.hslHueF(), max: 1.0); |
28 | m_hsva.s = qBound(min: .0, val: sv.first, max: 1.0); |
29 | m_hsva.v = qBound(min: .0, val: sv.second, max: 1.0); |
30 | } else { |
31 | m_hsva.h = qBound(min: .0, val: c.hsvHueF(), max: 1.0); |
32 | m_hsva.s = qBound(min: .0, val: c.hsvSaturationF(), max: 1.0); |
33 | m_hsva.v = qBound(min: .0, val: c.valueF(), max: 1.0); |
34 | } |
35 | |
36 | m_hsva.a = c.alphaF(); |
37 | |
38 | emit colorChanged(c: color()); |
39 | } |
40 | |
41 | int QQuickColorInputs::red() const |
42 | { |
43 | return color().red(); |
44 | } |
45 | |
46 | int QQuickColorInputs::green() const |
47 | { |
48 | return color().green(); |
49 | } |
50 | |
51 | int QQuickColorInputs::blue() const |
52 | { |
53 | return color().blue(); |
54 | } |
55 | |
56 | qreal QQuickColorInputs::alpha() const |
57 | { |
58 | return m_hsva.a; |
59 | } |
60 | |
61 | qreal QQuickColorInputs::hue() const |
62 | { |
63 | return m_hsva.h; |
64 | } |
65 | |
66 | qreal QQuickColorInputs::hslSaturation() const |
67 | { |
68 | return getSaturationAndLightness(saturation: m_hsva.s, value: m_hsva.v).first; |
69 | } |
70 | |
71 | qreal QQuickColorInputs::hsvSaturation() const |
72 | { |
73 | return m_hsva.s; |
74 | } |
75 | |
76 | qreal QQuickColorInputs::value() const |
77 | { |
78 | return m_hsva.v; |
79 | } |
80 | |
81 | qreal QQuickColorInputs::lightness() const |
82 | { |
83 | return getSaturationAndLightness(saturation: m_hsva.s, value: m_hsva.v).second; |
84 | } |
85 | |
86 | bool QQuickColorInputs::showAlpha() const |
87 | { |
88 | return m_showAlpha; |
89 | } |
90 | |
91 | void QQuickColorInputs::setShowAlpha(bool showAlpha) |
92 | { |
93 | if (m_showAlpha == showAlpha) |
94 | return; |
95 | |
96 | m_showAlpha = showAlpha; |
97 | emit showAlphaChanged(m_showAlpha); |
98 | } |
99 | |
100 | QQuickTextInput *QQuickColorInputs::hexInput() const |
101 | { |
102 | return m_hexInput; |
103 | } |
104 | |
105 | void QQuickColorInputs::setHexInput(QQuickTextInput *hexInput) |
106 | { |
107 | if (m_hexInput == hexInput) |
108 | return; |
109 | |
110 | if (m_hexInput) |
111 | disconnect(sender: m_hexInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHexChanged); |
112 | |
113 | m_hexInput = hexInput; |
114 | |
115 | if (m_hexInput) |
116 | connect(sender: m_hexInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHexChanged); |
117 | |
118 | emit hexInputChanged(); |
119 | } |
120 | |
121 | QQuickTextInput *QQuickColorInputs::redInput() const |
122 | { |
123 | return m_redInput; |
124 | } |
125 | |
126 | void QQuickColorInputs::setRedInput(QQuickTextInput *redInput) |
127 | { |
128 | if (m_redInput == redInput) |
129 | return; |
130 | |
131 | if (m_redInput) |
132 | disconnect(sender: m_redInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleRedChanged); |
133 | |
134 | m_redInput = redInput; |
135 | |
136 | if (m_redInput) |
137 | connect(sender: m_redInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleRedChanged); |
138 | |
139 | emit redInputChanged(); |
140 | } |
141 | |
142 | QQuickTextInput *QQuickColorInputs::greenInput() const |
143 | { |
144 | return m_greenInput; |
145 | } |
146 | |
147 | void QQuickColorInputs::setGreenInput(QQuickTextInput *greenInput) |
148 | { |
149 | if (m_greenInput == greenInput) |
150 | return; |
151 | |
152 | if (m_greenInput) |
153 | disconnect(sender: m_greenInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleGreenChanged); |
154 | |
155 | m_greenInput = greenInput; |
156 | |
157 | if (m_greenInput) |
158 | connect(sender: m_greenInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleGreenChanged); |
159 | |
160 | emit greenInputChanged(); |
161 | } |
162 | |
163 | QQuickTextInput *QQuickColorInputs::blueInput() const |
164 | { |
165 | return m_blueInput; |
166 | } |
167 | |
168 | void QQuickColorInputs::setBlueInput(QQuickTextInput *blueInput) |
169 | { |
170 | if (m_blueInput == blueInput) |
171 | return; |
172 | |
173 | if (m_blueInput) |
174 | disconnect(sender: m_blueInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleBlueChanged); |
175 | |
176 | m_blueInput = blueInput; |
177 | |
178 | if (m_blueInput) |
179 | connect(sender: m_blueInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleBlueChanged); |
180 | |
181 | emit blueInputChanged(); |
182 | } |
183 | |
184 | QQuickTextInput *QQuickColorInputs::hsvHueInput() const |
185 | { |
186 | return m_hsvHueInput; |
187 | } |
188 | |
189 | void QQuickColorInputs::setHsvHueInput(QQuickTextInput *hsvHueInput) |
190 | { |
191 | if (m_hsvHueInput == hsvHueInput) |
192 | return; |
193 | |
194 | if (m_hsvHueInput) |
195 | disconnect(sender: m_hsvHueInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHsvHueChanged); |
196 | |
197 | m_hsvHueInput = hsvHueInput; |
198 | |
199 | if (m_hsvHueInput) |
200 | connect(sender: m_hsvHueInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHsvHueChanged); |
201 | |
202 | emit hsvHueInputChanged(); |
203 | } |
204 | |
205 | QQuickTextInput *QQuickColorInputs::hslHueInput() const |
206 | { |
207 | return m_hslHueInput; |
208 | } |
209 | |
210 | void QQuickColorInputs::setHslHueInput(QQuickTextInput *hslHueInput) |
211 | { |
212 | if (m_hslHueInput == hslHueInput) |
213 | return; |
214 | |
215 | if (m_hslHueInput) |
216 | disconnect(sender: m_hslHueInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHslHueChanged); |
217 | |
218 | m_hslHueInput = hslHueInput; |
219 | |
220 | if (m_hslHueInput) |
221 | connect(sender: m_hslHueInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHslHueChanged); |
222 | |
223 | emit hslHueInputChanged(); |
224 | } |
225 | |
226 | QQuickTextInput *QQuickColorInputs::hsvSaturationInput() const |
227 | { |
228 | return m_hsvSaturationInput; |
229 | } |
230 | |
231 | void QQuickColorInputs::setHsvSaturationInput(QQuickTextInput *hsvSaturationInput) |
232 | { |
233 | if (m_hsvSaturationInput == hsvSaturationInput) |
234 | return; |
235 | |
236 | if (m_hsvSaturationInput) |
237 | disconnect(sender: m_hsvSaturationInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHsvSaturationChanged); |
238 | |
239 | m_hsvSaturationInput = hsvSaturationInput; |
240 | |
241 | if (m_hsvSaturationInput) |
242 | connect(sender: m_hsvSaturationInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHsvSaturationChanged); |
243 | |
244 | emit hsvSaturationInputChanged(); |
245 | } |
246 | |
247 | QQuickTextInput *QQuickColorInputs::hslSaturationInput() const |
248 | { |
249 | return m_hslSaturationInput; |
250 | } |
251 | |
252 | void QQuickColorInputs::setHslSaturationInput(QQuickTextInput *hslSaturationInput) |
253 | { |
254 | if (m_hslSaturationInput == hslSaturationInput) |
255 | return; |
256 | |
257 | if (m_hslSaturationInput) |
258 | disconnect(sender: m_hslSaturationInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHslSaturationChanged); |
259 | |
260 | m_hslSaturationInput = hslSaturationInput; |
261 | |
262 | if (m_hslSaturationInput) |
263 | connect(sender: m_hslSaturationInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHslSaturationChanged); |
264 | |
265 | emit hslSaturationInputChanged(); |
266 | } |
267 | |
268 | QQuickTextInput *QQuickColorInputs::valueInput() const |
269 | { |
270 | return m_valueInput; |
271 | } |
272 | |
273 | void QQuickColorInputs::setValueInput(QQuickTextInput *valueInput) |
274 | { |
275 | if (m_valueInput == valueInput) |
276 | return; |
277 | |
278 | if (m_valueInput) |
279 | disconnect(sender: m_valueInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleValueChanged); |
280 | |
281 | m_valueInput = valueInput; |
282 | |
283 | if (m_valueInput) |
284 | connect(sender: m_valueInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleValueChanged); |
285 | |
286 | emit valueInputChanged(); |
287 | } |
288 | |
289 | QQuickTextInput *QQuickColorInputs::lightnessInput() const |
290 | { |
291 | return m_lightnessInput; |
292 | } |
293 | |
294 | void QQuickColorInputs::setLightnessInput(QQuickTextInput *lightnessInput) |
295 | { |
296 | if (m_lightnessInput == lightnessInput) |
297 | return; |
298 | |
299 | if (m_lightnessInput) |
300 | disconnect(sender: m_lightnessInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleLightnessChanged); |
301 | |
302 | m_lightnessInput = lightnessInput; |
303 | |
304 | if (m_lightnessInput) |
305 | connect(sender: m_lightnessInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleLightnessChanged); |
306 | |
307 | emit lightnessInputChanged(); |
308 | } |
309 | |
310 | QQuickTextInput *QQuickColorInputs::rgbAlphaInput() const |
311 | { |
312 | return m_rgbAlphaInput; |
313 | } |
314 | |
315 | void QQuickColorInputs::setRgbAlphaInput(QQuickTextInput *alphaInput) |
316 | { |
317 | if (alphaInput == m_rgbAlphaInput) |
318 | return; |
319 | |
320 | if (m_rgbAlphaInput) { |
321 | disconnect(sender: m_rgbAlphaInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleRgbAlphaChanged); |
322 | disconnect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, receiver: m_rgbAlphaInput, slot: &QQuickTextInput::setVisible); |
323 | } |
324 | |
325 | m_rgbAlphaInput = alphaInput; |
326 | |
327 | if (m_rgbAlphaInput) { |
328 | connect(sender: m_rgbAlphaInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleRgbAlphaChanged); |
329 | connect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, context: m_rgbAlphaInput, slot: &QQuickTextInput::setVisible); |
330 | m_rgbAlphaInput->setVisible(showAlpha()); |
331 | } |
332 | |
333 | emit rgbAlphaInputChanged(); |
334 | } |
335 | |
336 | QQuickTextInput *QQuickColorInputs::hsvAlphaInput() const |
337 | { |
338 | return m_hsvAlphaInput; |
339 | } |
340 | |
341 | void QQuickColorInputs::setHsvAlphaInput(QQuickTextInput *alphaInput) |
342 | { |
343 | if (alphaInput == m_hsvAlphaInput) |
344 | return; |
345 | |
346 | if (m_hsvAlphaInput) { |
347 | disconnect(sender: m_hsvAlphaInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHsvAlphaChanged); |
348 | disconnect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, receiver: m_hsvAlphaInput, slot: &QQuickTextInput::setVisible); |
349 | } |
350 | |
351 | m_hsvAlphaInput = alphaInput; |
352 | |
353 | if (m_hsvAlphaInput) { |
354 | connect(sender: m_hsvAlphaInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHsvAlphaChanged); |
355 | connect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, context: m_hsvAlphaInput, slot: &QQuickTextInput::setVisible); |
356 | m_hsvAlphaInput->setVisible(showAlpha()); |
357 | } |
358 | |
359 | emit hsvAlphaInputChanged(); |
360 | } |
361 | |
362 | QQuickTextInput *QQuickColorInputs::hslAlphaInput() const |
363 | { |
364 | return m_hslAlphaInput; |
365 | } |
366 | |
367 | void QQuickColorInputs::setHslAlphaInput(QQuickTextInput *alphaInput) |
368 | { |
369 | if (alphaInput == m_hslAlphaInput) |
370 | return; |
371 | |
372 | if (m_hslAlphaInput) { |
373 | disconnect(sender: m_hslAlphaInput, signal: &QQuickTextInput::editingFinished, receiver: this, slot: &QQuickColorInputs::handleHslAlphaChanged); |
374 | disconnect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, receiver: m_hslAlphaInput, slot: &QQuickTextInput::setVisible); |
375 | } |
376 | |
377 | m_hslAlphaInput = alphaInput; |
378 | |
379 | if (m_hslAlphaInput) { |
380 | connect(sender: m_hslAlphaInput, signal: &QQuickTextInput::editingFinished, context: this, slot: &QQuickColorInputs::handleHslAlphaChanged); |
381 | connect(sender: this, signal: &QQuickColorInputs::showAlphaChanged, context: m_hslAlphaInput, slot: &QQuickTextInput::setVisible); |
382 | m_hslAlphaInput->setVisible(showAlpha()); |
383 | } |
384 | |
385 | emit hslAlphaInputChanged(); |
386 | } |
387 | |
388 | void QQuickColorInputs::handleHexChanged() |
389 | { |
390 | emit colorModified(c: QColor::fromString(name: m_hexInput->text())); |
391 | } |
392 | |
393 | void QQuickColorInputs::handleRedChanged() |
394 | { |
395 | QColor c = color(); |
396 | c.setRed(qBound(min: 0, val: m_redInput->text().toInt(), max: 255)); |
397 | emit colorModified(c); |
398 | } |
399 | |
400 | void QQuickColorInputs::handleGreenChanged() |
401 | { |
402 | QColor c = color(); |
403 | c.setGreen(qBound(min: 0, val: m_greenInput->text().toInt(), max: 255)); |
404 | emit colorModified(c); |
405 | } |
406 | |
407 | void QQuickColorInputs::handleBlueChanged() |
408 | { |
409 | QColor c = color(); |
410 | c.setBlue(qBound(min: 0, val: m_blueInput->text().toInt(), max: 255)); |
411 | emit colorModified(c); |
412 | } |
413 | |
414 | static QString s_percentage_pattern = QString::fromUtf8(utf8: "^(\\d+)%?$" ); |
415 | static QString s_degree_pattern = QString::fromUtf8(utf8: "(\\d+)°?$" ); |
416 | |
417 | void QQuickColorInputs::handleHsvHueChanged() |
418 | { |
419 | const QRegularExpression pattern(s_degree_pattern); |
420 | const auto match = pattern.match(subject: m_hsvHueInput->text()); |
421 | if (match.hasMatch()) { |
422 | const auto substr = match.captured(nth: 1); |
423 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 360)) / static_cast<qreal>(360); |
424 | emit colorModified(c: QColor::fromHsvF(h: input, s: hsvSaturation(), v: value(), a: alpha())); |
425 | } |
426 | } |
427 | |
428 | void QQuickColorInputs::handleHslHueChanged() |
429 | { |
430 | const QRegularExpression pattern(s_degree_pattern); |
431 | const auto match = pattern.match(subject: m_hslHueInput->text()); |
432 | if (match.hasMatch()) { |
433 | const auto substr = match.captured(nth: 1); |
434 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 360)) / static_cast<qreal>(360); |
435 | emit colorModified(c: QColor::fromHslF(h: input, s: hslSaturation(), l: lightness(), a: alpha())); |
436 | } |
437 | } |
438 | |
439 | void QQuickColorInputs::handleHsvSaturationChanged() |
440 | { |
441 | const QRegularExpression pattern(s_percentage_pattern); |
442 | const auto match = pattern.match(subject: m_hsvSaturationInput->text()); |
443 | if (match.hasMatch()) { |
444 | const auto substr = match.captured(nth: 1); |
445 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 100)) / static_cast<qreal>(100); |
446 | emit colorModified(c: QColor::fromHsvF(h: hue(), s: input, v: value(), a: alpha())); |
447 | } |
448 | } |
449 | |
450 | void QQuickColorInputs::handleHslSaturationChanged() |
451 | { |
452 | const QRegularExpression pattern(s_percentage_pattern); |
453 | const auto match = pattern.match(subject: m_hslSaturationInput->text()); |
454 | if (match.hasMatch()) { |
455 | const auto substr = match.captured(nth: 1); |
456 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 100)) / static_cast<qreal>(100); |
457 | emit colorModified(c: QColor::fromHslF(h: hue(), s: input, l: lightness(), a: alpha())); |
458 | } |
459 | } |
460 | |
461 | void QQuickColorInputs::handleValueChanged() |
462 | { |
463 | const QRegularExpression pattern(s_percentage_pattern); |
464 | const auto match = pattern.match(subject: m_valueInput->text()); |
465 | if (match.hasMatch()) { |
466 | const auto substr = match.captured(nth: 1); |
467 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 100)) / static_cast<qreal>(100); |
468 | emit colorModified(c: QColor::fromHsvF(h: hue(), s: hsvSaturation(), v: input, a: alpha())); |
469 | } |
470 | } |
471 | |
472 | void QQuickColorInputs::handleLightnessChanged() |
473 | { |
474 | const QRegularExpression pattern(s_percentage_pattern); |
475 | const auto match = pattern.match(subject: m_lightnessInput->text()); |
476 | if (match.hasMatch()) { |
477 | const auto substr = match.captured(nth: 1); |
478 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 100)) / static_cast<qreal>(100); |
479 | emit colorModified(c: QColor::fromHslF(h: hue(), s: hslSaturation(), l: input, a: alpha())); |
480 | } |
481 | } |
482 | |
483 | void QQuickColorInputs::handleRgbAlphaChanged() |
484 | { |
485 | handleAlphaChanged(input: m_rgbAlphaInput->text()); |
486 | } |
487 | |
488 | void QQuickColorInputs::handleHsvAlphaChanged() |
489 | { |
490 | handleAlphaChanged(input: m_hsvAlphaInput->text()); |
491 | } |
492 | |
493 | void QQuickColorInputs::handleHslAlphaChanged() |
494 | { |
495 | handleAlphaChanged(input: m_hslAlphaInput->text()); |
496 | } |
497 | |
498 | void QQuickColorInputs::handleAlphaChanged(const QString &input) |
499 | { |
500 | const QRegularExpression pattern(s_percentage_pattern); |
501 | const auto match = pattern.match(subject: input); |
502 | if (match.hasMatch()) { |
503 | QColor c = color(); |
504 | const auto substr = match.captured(nth: 1); |
505 | const qreal input = static_cast<qreal>(qBound(min: 0, val: substr.toInt(), max: 100)) / static_cast<qreal>(100); |
506 | c.setAlphaF(input); |
507 | emit colorModified(c); |
508 | } |
509 | } |
510 | |
511 | QT_END_NAMESPACE |
512 | |