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 "qquickabstractcolorpicker_p_p.h"
5
6#include "qquickcolordialogutils_p.h"
7
8#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
9#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
10
11#include <qpa/qplatformintegration.h>
12#include <private/qguiapplication_p.h>
13
14QQuickAbstractColorPickerPrivate::QQuickAbstractColorPickerPrivate() = default;
15
16bool QQuickAbstractColorPickerPrivate::handlePress(const QPointF &point, ulong timestamp)
17{
18 Q_Q(QQuickAbstractColorPicker);
19 QQuickControlPrivate::handlePress(point, timestamp);
20 m_pressPoint = point;
21 q->setPressed(true);
22 q->updateColor(pos: point);
23 return true;
24}
25
26bool QQuickAbstractColorPickerPrivate::handleMove(const QPointF &point, ulong timestamp)
27{
28 Q_Q(QQuickAbstractColorPicker);
29 QQuickControlPrivate::handleMove(point, timestamp);
30 if (point != m_pressPoint)
31 q->updateColor(pos: point);
32 return true;
33}
34
35bool QQuickAbstractColorPickerPrivate::handleRelease(const QPointF &point, ulong timestamp)
36{
37 Q_Q(QQuickAbstractColorPicker);
38 QQuickControlPrivate::handleRelease(point, timestamp);
39 m_pressPoint = QPointF();
40 q->setKeepMouseGrab(false);
41 q->setKeepTouchGrab(false);
42 q->setPressed(false);
43 q->updateColor(pos: point);
44 return true;
45}
46
47void QQuickAbstractColorPickerPrivate::handleUngrab()
48{
49 Q_Q(QQuickAbstractColorPicker);
50 QQuickControlPrivate::handleUngrab();
51 m_pressPoint = QPointF();
52 q->setPressed(false);
53}
54
55void QQuickAbstractColorPickerPrivate::cancelHandle()
56{
57 Q_Q(QQuickAbstractColorPicker);
58 quickCancelDeferred(object: q, property: handleName());
59}
60
61void QQuickAbstractColorPickerPrivate::executeHandle(bool complete)
62{
63 Q_Q(QQuickAbstractColorPicker);
64 if (m_handle.wasExecuted())
65 return;
66
67 if (!m_handle || complete)
68 quickBeginDeferred(object: q, property: handleName(), delegate&: m_handle);
69 if (complete)
70 quickCompleteDeferred(object: q, property: handleName(), delegate&: m_handle);
71}
72
73void QQuickAbstractColorPickerPrivate::itemImplicitWidthChanged(QQuickItem *item)
74{
75 Q_Q(QQuickAbstractColorPicker);
76 QQuickControlPrivate::itemImplicitWidthChanged(item);
77 if (item == m_handle)
78 emit q->implicitHandleWidthChanged();
79}
80
81void QQuickAbstractColorPickerPrivate::itemImplicitHeightChanged(QQuickItem *item)
82{
83 Q_Q(QQuickAbstractColorPicker);
84 QQuickControlPrivate::itemImplicitHeightChanged(item);
85 if (item == m_handle)
86 emit q->implicitHandleHeightChanged();
87}
88
89QQuickAbstractColorPicker::QQuickAbstractColorPicker(QQuickAbstractColorPickerPrivate &dd,
90 QQuickItem *parent)
91 : QQuickControl(dd, parent)
92{
93 setActiveFocusOnTab(true);
94 setAcceptedMouseButtons(Qt::LeftButton);
95}
96
97QQuickAbstractColorPicker::~QQuickAbstractColorPicker()
98{
99 Q_D(QQuickAbstractColorPicker);
100 if (d->m_handle)
101 d->removeImplicitSizeListener(item: d->m_handle);
102}
103
104QColor QQuickAbstractColorPicker::color() const
105{
106 Q_D(const QQuickAbstractColorPicker);
107 return d->m_hsl ? QColor::fromHslF(h: d->m_hsva.h, s: d->m_hsva.s, l: d->m_hsva.l, a: d->m_hsva.a)
108 : QColor::fromHsvF(h: d->m_hsva.h, s: d->m_hsva.s, v: d->m_hsva.v, a: d->m_hsva.a);
109}
110
111void QQuickAbstractColorPicker::setColor(const QColor &c)
112{
113 Q_D(QQuickAbstractColorPicker);
114 // QColor represents a theoretical color, rather than simply an rgba value.
115 // Therefore, two QColor objects can be different,
116 // and yet translate to the same rgba value.
117 // Since the color picker can reuse the same rgba value for multiple pixels,
118 // we should not return early if the rgba() values are equal,
119 // but only if the QColor objects are exactly the same.
120
121 if (color() == c)
122 return;
123
124 // When called from QQuickColorDialogImpl, it might not have the same spec as the current color
125 // picker.
126 if (d->m_hsl && c.spec() == QColor::Spec::Hsv) {
127 const auto sl = getSaturationAndLightness(saturation: c.hsvSaturationF(), value: c.valueF());
128 d->m_hsva.h = qBound(min: .0, val: c.hsvHueF(), max: 1.0);
129 d->m_hsva.s = qBound(min: .0, val: sl.first, max: 1.0);
130 d->m_hsva.l = qBound(min: .0, val: sl.second, max: 1.0);
131 } else if (!d->m_hsl && c.spec() == QColor::Spec::Hsl) {
132 const auto sv = getSaturationAndValue(saturation: c.hslSaturationF(), lightness: c.lightnessF());
133 d->m_hsva.h = qBound(min: .0, val: c.hslHueF(), max: 1.0);
134 d->m_hsva.s = qBound(min: .0, val: sv.first, max: 1.0);
135 d->m_hsva.v = qBound(min: .0, val: sv.second, max: 1.0);
136 } else {
137 d->m_hsva.h = qBound(min: .0, val: d->m_hsl ? c.hslHueF() : c.hsvHueF(), max: 1.0);
138 d->m_hsva.s = qBound(min: .0, val: d->m_hsl ? c.hslSaturationF() : c.hsvSaturationF(), max: 1.0);
139 d->m_hsva.v = qBound(min: .0, val: d->m_hsl ? c.lightnessF() : c.valueF(), max: 1.0);
140 }
141
142 d->m_hsva.a = qBound(min: .0, val: c.alphaF(), max: 1.0);
143
144 emit colorChanged(color: color());
145}
146
147qreal QQuickAbstractColorPicker::alpha() const
148{
149 Q_D(const QQuickAbstractColorPicker);
150 return d->m_hsva.a;
151}
152
153void QQuickAbstractColorPicker::setAlpha(qreal alpha)
154{
155 Q_D(QQuickAbstractColorPicker);
156
157 if (!qt_is_finite(d: alpha))
158 return;
159
160 alpha = qBound(min: .0, val: alpha, max: 1.0);
161
162 if (qFuzzyCompare(p1: d->m_hsva.a, p2: alpha))
163 return;
164
165 d->m_hsva.a = alpha;
166
167 emit colorChanged(color: color());
168}
169
170qreal QQuickAbstractColorPicker::hue() const
171{
172 Q_D(const QQuickAbstractColorPicker);
173 return d->m_hsva.h;
174}
175void QQuickAbstractColorPicker::setHue(qreal hue)
176{
177 Q_D(QQuickAbstractColorPicker);
178
179 if (!qt_is_finite(d: hue))
180 return;
181
182 d->m_hsva.h = hue;
183
184 emit colorChanged(color: color());
185}
186
187qreal QQuickAbstractColorPicker::saturation() const
188{
189 Q_D(const QQuickAbstractColorPicker);
190 return d->m_hsva.s;
191}
192
193void QQuickAbstractColorPicker::setSaturation(qreal saturation)
194{
195 Q_D(QQuickAbstractColorPicker);
196 if (!qt_is_finite(d: saturation))
197 return;
198
199 d->m_hsva.s = saturation;
200
201 emit colorChanged(color: color());
202}
203qreal QQuickAbstractColorPicker::value() const
204{
205 Q_D(const QQuickAbstractColorPicker);
206 return d->m_hsl ? getSaturationAndValue(saturation: d->m_hsva.s, lightness: d->m_hsva.l).second : d->m_hsva.v;
207}
208void QQuickAbstractColorPicker::setValue(qreal value)
209{
210 Q_D(QQuickAbstractColorPicker);
211 if (!qt_is_finite(d: value))
212 return;
213
214 const auto sv = d->m_hsl ? getSaturationAndValue(saturation: d->m_hsva.s, lightness: d->m_hsva.l)
215 : std::pair<qreal, qreal>(d->m_hsva.s, value);
216 d->m_hsva.s = sv.first;
217 d->m_hsva.v = sv.second;
218
219 emit colorChanged(color: color());
220}
221
222qreal QQuickAbstractColorPicker::lightness() const
223{
224 Q_D(const QQuickAbstractColorPicker);
225 return d->m_hsl ? d->m_hsva.l : getSaturationAndLightness(saturation: d->m_hsva.s, value: d->m_hsva.v).second;
226}
227void QQuickAbstractColorPicker::setLightness(qreal lightness)
228{
229 Q_D(QQuickAbstractColorPicker);
230 if (!qt_is_finite(d: lightness))
231 return;
232
233 const auto sl = !d->m_hsl ? getSaturationAndLightness(saturation: d->m_hsva.s, value: d->m_hsva.v)
234 : std::pair<qreal, qreal>(d->m_hsva.s, lightness);
235 d->m_hsva.s = sl.first;
236 d->m_hsva.l = sl.second;
237
238 emit colorChanged(color: color());
239}
240
241/*!
242 \internal
243
244 This property holds whether the slider is pressed.
245*/
246bool QQuickAbstractColorPicker::isPressed() const
247{
248 Q_D(const QQuickAbstractColorPicker);
249 return d->m_pressed;
250}
251
252void QQuickAbstractColorPicker::setPressed(bool pressed)
253{
254 Q_D(QQuickAbstractColorPicker);
255 if (pressed == d->m_pressed)
256 return;
257
258 d->m_pressed = pressed;
259 emit pressedChanged();
260}
261
262/*!
263 \internal
264
265 This property holds the handle item.
266*/
267QQuickItem *QQuickAbstractColorPicker::handle() const
268{
269 QQuickAbstractColorPickerPrivate *d = const_cast<QQuickAbstractColorPickerPrivate *>(d_func());
270 if (!d->m_handle)
271 d->executeHandle();
272 return d->m_handle;
273}
274
275void QQuickAbstractColorPicker::setHandle(QQuickItem *handle)
276{
277 Q_D(QQuickAbstractColorPicker);
278 if (handle == d->m_handle)
279 return;
280
281 if (!d->m_handle.isExecuting())
282 d->cancelHandle();
283
284 const qreal oldImplicitHandleWidth = implicitHandleWidth();
285 const qreal oldImplicitHandleHeight = implicitHandleHeight();
286
287 d->removeImplicitSizeListener(item: d->m_handle);
288 QQuickControlPrivate::hideOldItem(item: d->m_handle);
289 d->m_handle = handle;
290
291 if (handle) {
292 if (!handle->parentItem())
293 handle->setParentItem(this);
294 d->addImplicitSizeListener(item: handle);
295 }
296
297 if (!qFuzzyCompare(p1: oldImplicitHandleWidth, p2: implicitHandleWidth()))
298 emit implicitHandleWidthChanged();
299 if (!qFuzzyCompare(p1: oldImplicitHandleHeight, p2: implicitHandleHeight()))
300 emit implicitHandleHeightChanged();
301 if (!d->m_handle.isExecuting())
302 emit handleChanged();
303}
304
305/*!
306 \internal
307 \readonly
308
309 This property holds the implicit handle width.
310
311 The value is equal to \c {handle ? handle.implicitWidth : 0}.
312
313 This is typically used, together with \l {Control::}{implicitContentWidth} and
314 \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}.
315
316 \sa implicitHandleHeight
317*/
318qreal QQuickAbstractColorPicker::implicitHandleWidth() const
319{
320 Q_D(const QQuickAbstractColorPicker);
321 if (!d->m_handle)
322 return 0;
323 return d->m_handle->implicitWidth();
324}
325
326/*!
327 \internal
328 \readonly
329
330 This property holds the implicit handle height.
331
332 The value is equal to \c {handle ? handle.implicitHeight : 0}.
333
334 This is typically used, together with \l {Control::}{implicitContentHeight} and
335 \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}.
336
337 \sa implicitHandleWidth
338*/
339qreal QQuickAbstractColorPicker::implicitHandleHeight() const
340{
341 Q_D(const QQuickAbstractColorPicker);
342 if (!d->m_handle)
343 return 0;
344 return d->m_handle->implicitHeight();
345}
346
347void QQuickAbstractColorPicker::componentComplete()
348{
349 Q_D(QQuickAbstractColorPicker);
350 d->executeHandle(complete: true);
351 QQuickControl::componentComplete();
352}
353
354void QQuickAbstractColorPicker::updateColor(const QPointF &pos)
355{
356 QColor c = colorAt(pos);
357 c.setAlphaF(alpha());
358 setColor(c);
359
360 emit colorPicked(color: c);
361}
362

source code of qtdeclarative/src/quickdialogs/quickdialogsquickimpl/qquickabstractcolorpicker.cpp